Skip to content

Commit f4a6bf3

Browse files
committed
Add static classes
1 parent 2c02ac3 commit f4a6bf3

File tree

21 files changed

+1044
-42
lines changed

21 files changed

+1044
-42
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,12 +395,12 @@ jobs:
395395
# Not using --all-features because that would enable e.g. gnustep
396396
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features ${{ env.FEATURES }},${{ env.UNSTABLE_FEATURES }}
397397

398-
- name: Test static selectors
398+
- name: Test static class and selectors
399399
if: ${{ !matrix.dinghy && (matrix.runtime || 'apple') == 'apple' }}
400400
uses: actions-rs/cargo@v1
401401
with:
402402
command: test
403-
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel
403+
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel,unstable-static-class
404404

405405
- name: Run assembly tests
406406
# Not run on GNUStep yet since a lot of function labels are mangled and

objc2-foundation/src/declare_macro.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,7 @@ macro_rules! declare_class {
696696
use $crate::__std::sync::Once;
697697

698698
use $crate::objc2::declare::ClassBuilder;
699-
use $crate::objc2::runtime::Protocol;
699+
use $crate::objc2::runtime::{Class, Protocol};
700700
static REGISTER_CLASS: Once = Once::new();
701701

702702
REGISTER_CLASS.call_once(|| {
@@ -737,7 +737,8 @@ macro_rules! declare_class {
737737
let _cls = builder.register();
738738
});
739739

740-
$crate::objc2::class!($name)
740+
// We just registered the class, so it should be available
741+
Class::get(stringify!($name)).unwrap()
741742
}
742743
}
743744

objc2/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
66

77
## Unreleased - YYYY-MM-DD
88

9+
### Added
10+
* Added the `"unstable-static-class"` and `"unstable-static-class-inlined"`
11+
feature flags to make the `class!` macro zero cost.
12+
913

1014
## 0.3.0-beta.1 - 2022-07-19
1115

objc2/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ malloc = ["malloc_buf"]
5151
# https://github.com/madsmtm/objc2/issues/new
5252
unstable-static-sel = ["objc2-proc-macros"]
5353
unstable-static-sel-inlined = ["unstable-static-sel"]
54+
unstable-static-class = ["objc2-proc-macros"]
55+
unstable-static-class-inlined = ["unstable-static-class"]
5456

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

objc2/examples/introspection.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ use objc2::{class, msg_send, msg_send_id};
44
#[cfg(feature = "malloc")]
55
use objc2::{sel, Encode};
66

7+
#[cfg(feature = "apple")]
8+
#[link(name = "Foundation", kind = "framework")]
9+
extern "C" {}
10+
711
fn main() {
812
// Get a class
913
let cls = class!(NSObject);

objc2/examples/talk_to_me.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ use std::ffi::c_void;
1212
#[cfg(feature = "apple")]
1313
#[link(name = "AVFoundation", kind = "framework")]
1414
extern "C" {}
15+
#[cfg(feature = "apple")]
16+
#[link(name = "Foundation", kind = "framework")]
17+
extern "C" {}
1518

1619
const UTF8_ENCODING: NSUInteger = 4;
1720

objc2/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,11 @@ mod test_utils;
218218
#[cfg(feature = "malloc")]
219219
mod verify;
220220

221+
// Hack to make doctests work
222+
#[cfg(all(feature = "apple", feature = "unstable-static-class"))]
223+
#[link(name = "Foundation", kind = "framework")]
224+
extern "C" {}
225+
221226
/// Hacky way to make GNUStep link properly to Foundation while testing.
222227
///
223228
/// This is a temporary solution to make our CI work for now!

objc2/src/macros.rs

Lines changed: 138 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,15 @@
1717
/// ```
1818
#[macro_export]
1919
macro_rules! class {
20+
($name:ident) => {{
21+
$crate::__class_inner!($name)
22+
}};
23+
}
24+
25+
#[doc(hidden)]
26+
#[macro_export]
27+
#[cfg(not(feature = "unstable-static-class"))]
28+
macro_rules! __class_inner {
2029
($name:ident) => {{
2130
use $crate::__macro_helpers::{concat, panic, stringify, CachedClass, None, Some};
2231
static CACHED_CLASS: CachedClass = CachedClass::new();
@@ -25,7 +34,7 @@ macro_rules! class {
2534
let cls = unsafe { CACHED_CLASS.get(name) };
2635
match cls {
2736
Some(cls) => cls,
28-
None => panic!("Class with name {} could not be found", stringify!($name)),
37+
None => panic!("Class with name {} could not be found", stringify!($name),),
2938
}
3039
}};
3140
}
@@ -165,18 +174,12 @@ macro_rules! __sel_inner {
165174

166175
#[doc(hidden)]
167176
#[macro_export]
168-
macro_rules! __sel_inner_statics_apple_generic {
177+
macro_rules! __inner_statics_apple_generic {
169178
{
179+
@image_info;
170180
$image_info_section:literal;
171-
$var_name_section:literal;
172-
$selector_ref_section:literal;
173-
$data:ident,
174181
$($idents:ident)+
175182
} => {
176-
use $crate::__macro_helpers::{__hash_idents, u8, UnsafeCell};
177-
use $crate::ffi::__ImageInfo;
178-
use $crate::runtime::Sel;
179-
180183
/// We always emit the image info tag, since we need it to:
181184
/// - End up in the same codegen unit as the other statics below.
182185
/// - End up in the final binary so it can be read by dyld.
@@ -185,9 +188,22 @@ macro_rules! __sel_inner_statics_apple_generic {
185188
/// reports `__DATA/__objc_imageinfo has unexpectedly large size XXX`,
186189
/// but things still seems to work.
187190
#[link_section = $image_info_section]
188-
#[export_name = concat!("\x01L_OBJC_IMAGE_INFO_", __hash_idents!($($idents)+))]
191+
#[export_name = $crate::__macro_helpers::concat!(
192+
"\x01L_OBJC_IMAGE_INFO_",
193+
$crate::__macro_helpers::__hash_idents!($($idents)+)
194+
)]
189195
#[used] // Make sure this reaches the linker
190-
static _IMAGE_INFO: __ImageInfo = __ImageInfo::system();
196+
static _IMAGE_INFO: $crate::ffi::__ImageInfo = $crate::ffi::__ImageInfo::system();
197+
};
198+
{
199+
@sel;
200+
$var_name_section:literal;
201+
$selector_ref_section:literal;
202+
$data:ident,
203+
$($idents:ident)+
204+
} => {
205+
use $crate::__macro_helpers::{__hash_idents, u8, UnsafeCell};
206+
use $crate::runtime::Sel;
191207

192208
const X: &[u8] = $data.as_bytes();
193209

@@ -241,47 +257,108 @@ macro_rules! __sel_inner_statics_apple_generic {
241257
UnsafeCell::new(Sel::__internal_from_ptr(NAME_DATA.as_ptr().cast()))
242258
};
243259
};
260+
{
261+
@class;
262+
$class_ref_section:literal;
263+
$name:ident
264+
} => {
265+
use $crate::__macro_helpers::{concat, stringify, __hash_idents, UnsafeCell};
266+
use $crate::runtime::Class;
267+
268+
// TODO
269+
extern "C" {
270+
// TODO: Weak linkage?
271+
// https://stackoverflow.com/a/16936512
272+
// http://sealiesoftware.com/blog/archive/2010/4/8/Do-it-yourself_Objective-C_weak_import.html
273+
#[link_name = concat!("OBJC_CLASS_$_", stringify!($name))]
274+
static CLASS: Class;
275+
}
276+
277+
// TODO
278+
#[link_section = $class_ref_section]
279+
#[export_name = concat!("\x01L_OBJC_CLASSLIST_REFERENCES_$_", __hash_idents!($name))]
280+
static mut REF: UnsafeCell<&Class> = unsafe {
281+
UnsafeCell::new(&CLASS)
282+
};
283+
};
244284
}
245285

286+
// These sections are found by reading clang/LLVM sources
246287
#[doc(hidden)]
247288
#[macro_export]
248289
#[cfg(all(feature = "apple", not(all(target_os = "macos", target_arch = "x86"))))]
249-
macro_rules! __sel_inner_statics {
250-
($($args:tt)*) => {
251-
// Found by reading clang/LLVM sources
252-
$crate::__sel_inner_statics_apple_generic! {
290+
macro_rules! __inner_statics {
291+
(@image_info $($args:tt)*) => {
292+
$crate::__inner_statics_apple_generic! {
293+
@image_info;
253294
"__DATA,__objc_imageinfo,regular,no_dead_strip";
295+
$($args)*
296+
}
297+
};
298+
(@sel $($args:tt)*) => {
299+
$crate::__inner_statics_apple_generic! {
300+
@sel;
254301
"__TEXT,__objc_methname,cstring_literals";
255302
"__DATA,__objc_selrefs,literal_pointers,no_dead_strip";
256303
$($args)*
257304
}
258305
};
306+
(@class $($args:tt)*) => {
307+
$crate::__inner_statics_apple_generic! {
308+
@class;
309+
"__DATA,__objc_classrefs,regular,no_dead_strip";
310+
$($args)*
311+
}
312+
};
259313
}
260314

261315
#[doc(hidden)]
262316
#[macro_export]
263317
#[cfg(all(feature = "apple", target_os = "macos", target_arch = "x86"))]
264-
macro_rules! __sel_inner_statics {
265-
($($args:tt)*) => {
266-
$crate::__sel_inner_statics_apple_generic! {
318+
macro_rules! __inner_statics {
319+
(@image_info $($args:tt)*) => {
320+
$crate::__inner_statics_apple_generic! {
321+
@image_info;
267322
"__OBJC,__image_info,regular";
323+
$($args)*
324+
}
325+
};
326+
(@sel $($args:tt)*) => {
327+
$crate::__inner_statics_apple_generic! {
328+
@sel;
268329
"__TEXT,__cstring,cstring_literals";
269330
"__OBJC,__message_refs,literal_pointers,no_dead_strip";
270331
$($args)*
271332
}
272333
};
334+
(@class $($args:tt)*) => {
335+
// TODO
336+
$crate::__macro_helpers::compile_error!(
337+
"The `\"unstable-static-class\"` feature is not yet supported on 32bit macOS!"
338+
)
339+
// TODO: module info
340+
};
273341
}
274342

275343
#[doc(hidden)]
276344
#[macro_export]
277345
#[cfg(not(feature = "apple"))]
278-
macro_rules! __sel_inner_statics {
279-
($($args:tt)*) => {
346+
macro_rules! __inner_statics {
347+
(@image_info $($args:tt)*) => {
348+
// TODO
349+
};
350+
(@sel $($args:tt)*) => {
280351
// TODO
281352
$crate::__macro_helpers::compile_error!(
282353
"The `\"unstable-static-sel\"` feature is not yet supported on GNUStep!"
283354
)
284355
};
356+
(@class $($args:tt)*) => {
357+
// TODO
358+
$crate::__macro_helpers::compile_error!(
359+
"The `\"unstable-static-class\"` feature is not yet supported on GNUStep!"
360+
)
361+
};
285362
}
286363

287364
#[doc(hidden)]
@@ -291,8 +368,9 @@ macro_rules! __sel_inner_statics {
291368
not(feature = "unstable-static-sel-inlined")
292369
))]
293370
macro_rules! __sel_inner {
294-
($($args:tt)*) => {{
295-
$crate::__sel_inner_statics!($($args)*);
371+
($data:ident, $($idents:ident)+) => {{
372+
$crate::__inner_statics!(@image_info $($idents)+);
373+
$crate::__inner_statics!(@sel $data, $($idents)+);
296374

297375
/// HACK: Wrap the access in a non-generic, `#[inline(never)]`
298376
/// function to make the compiler group it into the same codegen unit
@@ -319,8 +397,44 @@ macro_rules! __sel_inner {
319397
#[macro_export]
320398
#[cfg(all(feature = "unstable-static-sel-inlined"))]
321399
macro_rules! __sel_inner {
322-
($($args:tt)*) => {{
323-
$crate::__sel_inner_statics!($($args)*);
400+
($data:ident, $($idents:ident)+) => {{
401+
$crate::__inner_statics!(@image_info $($idents)+);
402+
$crate::__inner_statics!(@sel $data, $($idents)+);
403+
404+
#[allow(unused_unsafe)]
405+
// SAFETY: See above
406+
unsafe { *REF.get() }
407+
}};
408+
}
409+
410+
#[doc(hidden)]
411+
#[macro_export]
412+
#[cfg(all(
413+
feature = "unstable-static-class",
414+
not(feature = "unstable-static-class-inlined")
415+
))]
416+
macro_rules! __class_inner {
417+
($name:ident) => {{
418+
$crate::__inner_statics!(@image_info $name);
419+
$crate::__inner_statics!(@class $name);
420+
421+
// SAFETY: Same as __sel_inner
422+
#[inline(never)]
423+
fn objc_static_workaround() -> &'static Class {
424+
unsafe { *REF.get() }
425+
}
426+
427+
objc_static_workaround()
428+
}};
429+
}
430+
431+
#[doc(hidden)]
432+
#[macro_export]
433+
#[cfg(all(feature = "unstable-static-class-inlined"))]
434+
macro_rules! __class_inner {
435+
($name:ident) => {{
436+
$crate::__inner_statics!(@image_info $name);
437+
$crate::__inner_statics!(@class $name);
324438

325439
#[allow(unused_unsafe)]
326440
// SAFETY: See above

objc2/src/rc/test_object.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,11 @@ impl RcTestObject {
130130
builder.add_method(sel!(dealloc), dealloc as unsafe extern "C" fn(_, _));
131131
}
132132

133-
builder.register();
133+
let _cls = builder.register();
134134
});
135135

136-
class!(RcTestObject)
136+
// Can't use `class!` here since `RcTestObject` is dynamically created.
137+
Class::get("RcTestObject").unwrap()
137138
}
138139

139140
pub(crate) fn new() -> Id<Self, Owned> {

objc2/src/test_utils.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ use std::sync::Once;
55

66
use crate::declare::{ClassBuilder, ProtocolBuilder};
77
use crate::runtime::{Class, Object, Protocol, Sel};
8-
use crate::{class, msg_send, sel};
98
use crate::{ffi, Encode, Encoding, MessageReceiver};
9+
use crate::{msg_send, sel};
1010

1111
#[derive(Debug)]
1212
pub(crate) struct CustomObject {
@@ -173,7 +173,8 @@ pub(crate) fn custom_class() -> &'static Class {
173173
builder.register();
174174
});
175175

176-
class!(CustomObject)
176+
// Can't use `class!` here since `CustomObject` is dynamically created.
177+
Class::get("CustomObject").unwrap()
177178
}
178179

179180
pub(crate) fn custom_protocol() -> &'static Protocol {
@@ -232,7 +233,7 @@ pub(crate) fn custom_subclass() -> &'static Class {
232233
builder.register();
233234
});
234235

235-
class!(CustomSubclassObject)
236+
Class::get("CustomSubclassObject").unwrap()
236237
}
237238

238239
pub(crate) fn custom_subclass_object() -> CustomObject {

0 commit comments

Comments
 (0)