Skip to content

Commit 61b0a97

Browse files
authored
Merge pull request #104 from madsmtm/static-sel
Add static selectors behind the `"unstable-static-sel"` feature flag.
2 parents 0a6644a + 783a742 commit 61b0a97

File tree

36 files changed

+3324
-26
lines changed

36 files changed

+3324
-26
lines changed

.github/workflows/ci.yml

+7
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,13 @@ jobs:
378378
# Not using --all-features because that would enable e.g. gnustep
379379
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features ${{ env.FEATURES }},${{ env.UNSTABLE_FEATURES }}
380380

381+
- name: Test static selectors
382+
if: ${{ !matrix.dinghy && (matrix.runtime || 'apple') == 'apple' }}
383+
uses: actions-rs/cargo@v1
384+
with:
385+
command: test
386+
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel
387+
381388
- name: Run assembly tests
382389
# Not run on GNUStep yet since a lot of function labels are mangled and
383390
# not inlined (and hence quite hard to match on, at some point we'll

objc-sys/build.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,27 @@ fn main() {
5858
// The script doesn't depend on our code
5959
println!("cargo:rerun-if-changed=build.rs");
6060

61+
let target = env::var("TARGET").unwrap();
62+
6163
// Used to figure out when BOOL should be i8 vs. bool
62-
if env::var("TARGET").unwrap().ends_with("macabi") {
64+
// Matches:
65+
// aarch64-apple-ios-macabi
66+
// x86_64-apple-ios-macabi
67+
if target.ends_with("macabi") {
6368
println!("cargo:rustc-cfg=target_abi_macabi");
6469
}
6570

71+
// Used to set correct image info in `objc2`
72+
// Matches:
73+
// aarch64-apple-ios-sim
74+
// aarch64-apple-watchos-sim
75+
// x86_64-apple-watchos-sim
76+
// i386-apple-ios
77+
// x86_64-apple-ios
78+
if target.ends_with("sim") || target == "i386-apple-ios" || target == "x86_64-apple-ios" {
79+
println!("cargo:rustc-cfg=target_simulator");
80+
}
81+
6682
// TODO: Figure out when to enable this
6783
// println!("cargo:rustc-cfg=libobjc2_strict_apple_compat");
6884

objc-sys/src/image_info.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#[repr(C)]
2+
#[doc(hidden)] // Private for now
3+
pub struct __ImageInfo {
4+
// These are not actually `unsigned int`, even though the docs say so
5+
/// The version of the image info struct.
6+
version: u32,
7+
flags: u32,
8+
}
9+
10+
#[allow(unused)]
11+
impl __ImageInfo {
12+
/// Unused
13+
const FIX_AND_CONTINUE: u32 = 1 << 0;
14+
const SUPPORTS_GARBAGE_COLLECTED: u32 = 1 << 1;
15+
const REQUIRES_GARBAGE_COLLECTION: u32 = 1 << 2;
16+
const OPTIMIZED_BY_DYLD: u32 = 1 << 3; // TODO
17+
/// Unused
18+
const CORRECTED_SYNTHESIZE: u32 = 1 << 4;
19+
/// Whether we're compiling this to run on a simulator.
20+
const IMAGE_IS_SIMULATED: u32 = 1 << 5;
21+
/// Whether we are generating class properties.
22+
const CLASS_PROPERTIES: u32 = 1 << 6;
23+
const DYLD_PREOPTIMIZED: u32 = 1 << 7;
24+
25+
const SWIFT_ABI_VERSION_SHIFT: u32 = 8;
26+
const SWIFT_ABI_VERSION_MASK: u32 = 0xff << Self::SWIFT_ABI_VERSION_SHIFT;
27+
const SWIFT_MINOR_VERSION_SHIFT: u32 = 16;
28+
const SWIFT_MINOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MINOR_VERSION_SHIFT;
29+
const SWIFT_MAJOR_VERSION_SHIFT: u32 = 24;
30+
const SWIFT_MAJOR_VERSION_MASK: u32 = 0xff << Self::SWIFT_MAJOR_VERSION_SHIFT;
31+
32+
/// Fetches the image info for the current runtime + target combination
33+
#[inline]
34+
pub const fn system() -> Self {
35+
// We don't currently do anything relating to class properties, but
36+
// let's just mimic what Clang does!
37+
let mut flags = Self::CLASS_PROPERTIES;
38+
39+
if cfg!(target_simulator) {
40+
flags |= Self::IMAGE_IS_SIMULATED;
41+
}
42+
43+
Self { version: 0, flags }
44+
}
45+
}

objc-sys/src/lib.rs

+2
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ mod class;
100100
mod constants;
101101

102102
mod exception;
103+
mod image_info;
103104
mod message;
104105
mod method;
105106
mod object;
@@ -113,6 +114,7 @@ mod various;
113114
pub use class::*;
114115
pub use constants::*;
115116
pub use exception::*;
117+
pub use image_info::*;
116118
pub use message::*;
117119
pub use method::*;
118120
pub use object::*;

objc2-proc-macros/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "objc2-proc-macros"
33
# Remember to update html_root_url in lib.rs
44
version = "0.0.0"
5-
authors = ["Mads Marquart <[email protected]>"]
5+
authors = ["Mads Marquart <[email protected]>", "Calvin Watford"]
66
edition = "2021"
77

88
description = "Procedural macros for the objc2 project"

objc2-proc-macros/src/lib.rs

+64
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,67 @@
1616
#[cfg(doctest)]
1717
#[doc = include_str!("../README.md")]
1818
extern "C" {}
19+
20+
use core::hash::{Hash, Hasher};
21+
22+
use proc_macro::Ident;
23+
use proc_macro::Literal;
24+
use proc_macro::TokenStream;
25+
use proc_macro::TokenTree;
26+
27+
/// Quick n' dirty way to extract the idents given by the `sel!` macro.
28+
fn get_idents(input: TokenStream) -> impl Iterator<Item = Ident> {
29+
input.into_iter().flat_map(|token| {
30+
if let TokenTree::Group(group) = token {
31+
group
32+
.stream()
33+
.into_iter()
34+
.map(|token| {
35+
if let TokenTree::Ident(ident) = token {
36+
ident
37+
} else {
38+
panic!("Expected ident, got {:?}", token)
39+
}
40+
})
41+
.collect::<Vec<_>>()
42+
.into_iter()
43+
} else if let TokenTree::Ident(ident) = token {
44+
vec![ident].into_iter()
45+
} else {
46+
panic!("Expected group or ident, got {:?}", token)
47+
}
48+
})
49+
}
50+
51+
/// Creates a hash from the input and source code locations in the provided
52+
/// idents.
53+
///
54+
/// This hash is not guaranteed to be stable across compiler versions.
55+
///
56+
/// Tests are in [`objc2::__macro_helpers`].
57+
#[proc_macro]
58+
#[doc(hidden)]
59+
pub fn __hash_idents(input: TokenStream) -> TokenStream {
60+
// Create the hasher
61+
let mut hasher = std::collections::hash_map::DefaultHasher::new();
62+
63+
// Hash each ident
64+
for ident in get_idents(input) {
65+
ident.to_string().hash(&mut hasher);
66+
67+
// Hash the source code location of the ident
68+
//
69+
// HACK: the only somewhat-reasonable way to get "unique" data in a
70+
// proc macro right now is from the `Debug` formatter for spans which
71+
// includes the source code location... so just hash the whole `Debug`
72+
// format output of the span
73+
//
74+
// Prior art in the `defmt` crate, see here:
75+
// https://github.com/knurling-rs/defmt/blob/defmt-v0.3.1/macros/src/construct.rs
76+
format!("{:?}", ident.span()).hash(&mut hasher);
77+
}
78+
79+
// Get the hash from the hasher and return it as 16 hexadecimal characters
80+
let s = format!("{:016x}", hasher.finish());
81+
TokenTree::Literal(Literal::string(&s)).into()
82+
}

objc2/CHANGELOG.md

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
2525
msg_send_id![msg_send_id![Self::class(), alloc], new].unwrap()
2626
};
2727
```
28+
* Added the `"unstable-static-sel"` and `"unstable-static-sel-inlined"`
29+
feature flags to make the `sel!` macro (and by extension, the `msg_send!`
30+
macros) faster.
2831

2932

3033
## 0.3.0-beta.0 - 2022-06-13

objc2/Cargo.toml

+12
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@ verify_message = ["malloc"] # TODO: Remove malloc feature here
3737
# increases compilation time.
3838
malloc = ["malloc_buf"]
3939

40+
# Make the `sel!` macro look up the selector statically.
41+
#
42+
# The plan is to enable this by default, but right now we are uncertain of
43+
# it's stability, and it might need significant changes before being fully
44+
# ready!
45+
#
46+
# Please test it, and report any issues you may find:
47+
# https://github.com/madsmtm/objc2/issues/new
48+
unstable-static-sel = ["objc2-proc-macros"]
49+
unstable-static-sel-inlined = ["unstable-static-sel"]
50+
4051
# Uses nightly features to make AutoreleasePool zero-cost even in debug mode
4152
unstable-autoreleasesafe = []
4253

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

5668
[dev-dependencies]
5769
iai = { version = "0.1", git = "https://github.com/madsmtm/iai", branch = "callgrind" }

objc2/src/__macro_helpers.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ use crate::rc::{Id, Ownership};
22
use crate::runtime::{Class, Sel};
33
use crate::{Message, MessageArguments, MessageError, MessageReceiver};
44

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

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

43-
#[doc(hidden)]
4444
pub trait MsgSendId<T, U> {
4545
unsafe fn send_message_id<A: MessageArguments>(
4646
obj: T,
@@ -131,7 +131,6 @@ impl<T: MessageReceiver, U: Message, O: Ownership> MsgSendId<T, Id<U, O>>
131131
/// Checks whether a given selector is said to be in a given selector family.
132132
///
133133
/// <https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families>
134-
#[doc(hidden)]
135134
pub const fn in_selector_family(mut selector: &[u8], mut family: &[u8]) -> bool {
136135
// Skip leading underscores from selector
137136
loop {
@@ -373,4 +372,31 @@ mod tests {
373372
let _obj: Id<Object, Owned> = unsafe { msg_send_id![cls, new].unwrap() };
374373
}
375374
}
375+
376+
#[test]
377+
#[cfg(feature = "objc2-proc-macros")]
378+
fn hash_idents_different() {
379+
assert_ne!(__hash_idents!(abc), __hash_idents!(def));
380+
}
381+
382+
#[test]
383+
#[cfg(feature = "objc2-proc-macros")]
384+
fn hash_idents_same_no_equal() {
385+
assert_ne!(__hash_idents!(abc), __hash_idents!(abc));
386+
assert_ne!(__hash_idents!(abc def ghi), __hash_idents!(abc def ghi));
387+
}
388+
389+
#[test]
390+
#[cfg(feature = "objc2-proc-macros")]
391+
fn hash_idents_exact_same_ident() {
392+
macro_rules! x {
393+
($x:ident) => {
394+
(__hash_idents!($x), __hash_idents!($x))
395+
};
396+
}
397+
let (ident1, ident2) = x!(abc);
398+
// This is a limitation of `__hash_idents`, ideally we'd like these
399+
// to be different!
400+
assert_eq!(ident1, ident2);
401+
}
376402
}

0 commit comments

Comments
 (0)