Skip to content

Commit

Permalink
Merge pull request #104 from madsmtm/static-sel
Browse files Browse the repository at this point in the history
Add static selectors behind the `"unstable-static-sel"` feature flag.
  • Loading branch information
madsmtm committed Jun 23, 2022
2 parents 0a6644a + 783a742 commit 61b0a97
Show file tree
Hide file tree
Showing 36 changed files with 3,324 additions and 26 deletions.
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,13 @@ jobs:
# Not using --all-features because that would enable e.g. gnustep
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features ${{ env.FEATURES }},${{ env.UNSTABLE_FEATURES }}

- name: Test static selectors
if: ${{ !matrix.dinghy && (matrix.runtime || 'apple') == 'apple' }}
uses: actions-rs/cargo@v1
with:
command: test
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel

- name: Run assembly tests
# Not run on GNUStep yet since a lot of function labels are mangled and
# not inlined (and hence quite hard to match on, at some point we'll
Expand Down
18 changes: 17 additions & 1 deletion objc-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,27 @@ fn main() {
// The script doesn't depend on our code
println!("cargo:rerun-if-changed=build.rs");

let target = env::var("TARGET").unwrap();

// Used to figure out when BOOL should be i8 vs. bool
if env::var("TARGET").unwrap().ends_with("macabi") {
// Matches:
// aarch64-apple-ios-macabi
// x86_64-apple-ios-macabi
if target.ends_with("macabi") {
println!("cargo:rustc-cfg=target_abi_macabi");
}

// Used to set correct image info in `objc2`
// Matches:
// aarch64-apple-ios-sim
// aarch64-apple-watchos-sim
// x86_64-apple-watchos-sim
// i386-apple-ios
// x86_64-apple-ios
if target.ends_with("sim") || target == "i386-apple-ios" || target == "x86_64-apple-ios" {
println!("cargo:rustc-cfg=target_simulator");
}

// TODO: Figure out when to enable this
// println!("cargo:rustc-cfg=libobjc2_strict_apple_compat");

Expand Down
45 changes: 45 additions & 0 deletions objc-sys/src/image_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#[repr(C)]
#[doc(hidden)] // Private for now
pub struct __ImageInfo {
// These are not actually `unsigned int`, even though the docs say so
/// The version of the image info struct.
version: u32,
flags: u32,
}

#[allow(unused)]
impl __ImageInfo {
/// Unused
const FIX_AND_CONTINUE: u32 = 1 << 0;
const SUPPORTS_GARBAGE_COLLECTED: u32 = 1 << 1;
const REQUIRES_GARBAGE_COLLECTION: u32 = 1 << 2;
const OPTIMIZED_BY_DYLD: u32 = 1 << 3; // TODO
/// Unused
const CORRECTED_SYNTHESIZE: u32 = 1 << 4;
/// Whether we're compiling this to run on a simulator.
const IMAGE_IS_SIMULATED: u32 = 1 << 5;
/// Whether we are generating class properties.
const CLASS_PROPERTIES: u32 = 1 << 6;
const DYLD_PREOPTIMIZED: u32 = 1 << 7;

const SWIFT_ABI_VERSION_SHIFT: u32 = 8;
const SWIFT_ABI_VERSION_MASK: u32 = 0xff << Self::SWIFT_ABI_VERSION_SHIFT;
const SWIFT_MINOR_VERSION_SHIFT: u32 = 16;
const SWIFT_MINOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MINOR_VERSION_SHIFT;
const SWIFT_MAJOR_VERSION_SHIFT: u32 = 24;
const SWIFT_MAJOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MAJOR_VERSION_SHIFT;

/// Fetches the image info for the current runtime + target combination
#[inline]
pub const fn system() -> Self {
// We don't currently do anything relating to class properties, but
// let's just mimic what Clang does!
let mut flags = Self::CLASS_PROPERTIES;

if cfg!(target_simulator) {
flags |= Self::IMAGE_IS_SIMULATED;
}

Self { version: 0, flags }
}
}
2 changes: 2 additions & 0 deletions objc-sys/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ mod class;
mod constants;

mod exception;
mod image_info;
mod message;
mod method;
mod object;
Expand All @@ -113,6 +114,7 @@ mod various;
pub use class::*;
pub use constants::*;
pub use exception::*;
pub use image_info::*;
pub use message::*;
pub use method::*;
pub use object::*;
Expand Down
2 changes: 1 addition & 1 deletion objc2-proc-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "objc2-proc-macros"
# Remember to update html_root_url in lib.rs
version = "0.0.0"
authors = ["Mads Marquart <[email protected]>"]
authors = ["Mads Marquart <[email protected]>", "Calvin Watford"]
edition = "2021"

description = "Procedural macros for the objc2 project"
Expand Down
64 changes: 64 additions & 0 deletions objc2-proc-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,67 @@
#[cfg(doctest)]
#[doc = include_str!("../README.md")]
extern "C" {}

use core::hash::{Hash, Hasher};

use proc_macro::Ident;
use proc_macro::Literal;
use proc_macro::TokenStream;
use proc_macro::TokenTree;

/// Quick n' dirty way to extract the idents given by the `sel!` macro.
fn get_idents(input: TokenStream) -> impl Iterator<Item = Ident> {
input.into_iter().flat_map(|token| {
if let TokenTree::Group(group) = token {
group
.stream()
.into_iter()
.map(|token| {
if let TokenTree::Ident(ident) = token {
ident
} else {
panic!("Expected ident, got {:?}", token)
}
})
.collect::<Vec<_>>()
.into_iter()
} else if let TokenTree::Ident(ident) = token {
vec![ident].into_iter()
} else {
panic!("Expected group or ident, got {:?}", token)
}
})
}

/// Creates a hash from the input and source code locations in the provided
/// idents.
///
/// This hash is not guaranteed to be stable across compiler versions.
///
/// Tests are in [`objc2::__macro_helpers`].
#[proc_macro]
#[doc(hidden)]
pub fn __hash_idents(input: TokenStream) -> TokenStream {
// Create the hasher
let mut hasher = std::collections::hash_map::DefaultHasher::new();

// Hash each ident
for ident in get_idents(input) {
ident.to_string().hash(&mut hasher);

// Hash the source code location of the ident
//
// HACK: the only somewhat-reasonable way to get "unique" data in a
// proc macro right now is from the `Debug` formatter for spans which
// includes the source code location... so just hash the whole `Debug`
// format output of the span
//
// Prior art in the `defmt` crate, see here:
// https://github.com/knurling-rs/defmt/blob/defmt-v0.3.1/macros/src/construct.rs
format!("{:?}", ident.span()).hash(&mut hasher);
}

// Get the hash from the hasher and return it as 16 hexadecimal characters
let s = format!("{:016x}", hasher.finish());
TokenTree::Literal(Literal::string(&s)).into()
}
3 changes: 3 additions & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
msg_send_id![msg_send_id![Self::class(), alloc], new].unwrap()
};
```
* Added the `"unstable-static-sel"` and `"unstable-static-sel-inlined"`
feature flags to make the `sel!` macro (and by extension, the `msg_send!`
macros) faster.


## 0.3.0-beta.0 - 2022-06-13
Expand Down
12 changes: 12 additions & 0 deletions objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,17 @@ verify_message = ["malloc"] # TODO: Remove malloc feature here
# increases compilation time.
malloc = ["malloc_buf"]

# Make the `sel!` macro look up the selector statically.
#
# The plan is to enable this by default, but right now we are uncertain of
# it's stability, and it might need significant changes before being fully
# ready!
#
# Please test it, and report any issues you may find:
# https://github.com/madsmtm/objc2/issues/new
unstable-static-sel = ["objc2-proc-macros"]
unstable-static-sel-inlined = ["unstable-static-sel"]

# Uses nightly features to make AutoreleasePool zero-cost even in debug mode
unstable-autoreleasesafe = []

Expand All @@ -52,6 +63,7 @@ gnustep-2-1 = ["gnustep-2-0", "objc-sys/gnustep-2-1"]
malloc_buf = { version = "1.0", optional = true }
objc-sys = { path = "../objc-sys", version = "=0.2.0-beta.0", default-features = false }
objc2-encode = { path = "../objc2-encode", version = "=2.0.0-pre.0", default-features = false }
objc2-proc-macros = { path = "../objc2-proc-macros", optional = true }

[dev-dependencies]
iai = { version = "0.1", git = "https://github.com/madsmtm/iai", branch = "callgrind" }
Expand Down
34 changes: 30 additions & 4 deletions objc2/src/__macro_helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use crate::rc::{Id, Ownership};
use crate::runtime::{Class, Sel};
use crate::{Message, MessageArguments, MessageError, MessageReceiver};

#[doc(hidden)]
pub use core::cell::UnsafeCell;
pub use core::compile_error;
#[cfg(feature = "unstable-static-sel")]
pub use objc2_proc_macros::__hash_idents;

/// Helper for specifying the retain semantics for a given selector family.
///
Expand All @@ -28,7 +30,6 @@ pub use core::compile_error;
/// ARC though!
///
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments>
#[doc(hidden)]
pub struct RetainSemantics<
// `new` family
const NEW: bool,
Expand All @@ -40,7 +41,6 @@ pub struct RetainSemantics<
const COPY_OR_MUT_COPY: bool,
> {}

#[doc(hidden)]
pub trait MsgSendId<T, U> {
unsafe fn send_message_id<A: MessageArguments>(
obj: T,
Expand Down Expand Up @@ -131,7 +131,6 @@ impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, Id<U, O>>
/// Checks whether a given selector is said to be in a given selector family.
///
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families>
#[doc(hidden)]
pub const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
// Skip leading underscores from selector
loop {
Expand Down Expand Up @@ -373,4 +372,31 @@ mod tests {
let _obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new].unwrap() };
}
}

#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_different() {
assert_ne!(__hash_idents!(abc), __hash_idents!(def));
}

#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_same_no_equal() {
assert_ne!(__hash_idents!(abc), __hash_idents!(abc));
assert_ne!(__hash_idents!(abc def ghi), __hash_idents!(abc def ghi));
}

#[test]
#[cfg(feature = "objc2-proc-macros")]
fn hash_idents_exact_same_ident() {
macro_rules! x {
($x:ident) => {
(__hash_idents!($x), __hash_idents!($x))
};
}
let (ident1, ident2) = x!(abc);
// This is a limitation of `__hash_idents`, ideally we'd like these
// to be different!
assert_eq!(ident1, ident2);
}
}
Loading

0 comments on commit 61b0a97

Please sign in to comment.