Skip to content

Commit 0c9566b

Browse files
committed
Add static classes
1 parent 98e111c commit 0c9566b

File tree

14 files changed

+1003
-30
lines changed

14 files changed

+1003
-30
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -378,12 +378,12 @@ 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
381+
- name: Test static class and selectors
382382
if: ${{ !matrix.dinghy && (matrix.runtime || 'apple') == 'apple' }}
383383
uses: actions-rs/cargo@v1
384384
with:
385385
command: test
386-
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel
386+
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel,unstable-static-class
387387

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

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/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/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
@@ -145,10 +145,11 @@ impl RcTestObject {
145145
);
146146
}
147147

148-
builder.register();
148+
let _cls = builder.register();
149149
});
150150

151-
class!(RcTestObject)
151+
// Can't use `class!` here since `RcTestObject` is dynamically created.
152+
Class::get("RcTestObject").unwrap()
152153
}
153154

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

objc2/src/test_utils.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,8 @@ pub(crate) fn custom_class() -> &'static Class {
166166
builder.register();
167167
});
168168

169-
class!(CustomObject)
169+
// Can't use `class!` here since `CustomObject` is dynamically created.
170+
Class::get("CustomObject").unwrap()
170171
}
171172

172173
pub(crate) fn custom_protocol() -> &'static Protocol {
@@ -225,7 +226,7 @@ pub(crate) fn custom_subclass() -> &'static Class {
225226
builder.register();
226227
});
227228

228-
class!(CustomSubclassObject)
229+
Class::get("CustomSubclassObject").unwrap()
229230
}
230231

231232
pub(crate) fn custom_subclass_object() -> CustomObject {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
[package]
2+
name = "test_static_class"
3+
version = "0.1.0"
4+
edition = "2021"
5+
publish = false
6+
7+
[lib]
8+
path = "lib.rs"
9+
10+
[dependencies]
11+
objc2 = { path = "../../../objc2", default-features = false }
12+
13+
[features]
14+
default = ["apple", "std"]
15+
std = ["objc2/std"]
16+
# Runtime
17+
apple = ["objc2/apple"]
18+
gnustep-1-7 = ["objc2/gnustep-1-7"]
19+
gnustep-1-8 = ["gnustep-1-7", "objc2/gnustep-1-8"]
20+
gnustep-1-9 = ["gnustep-1-8", "objc2/gnustep-1-9"]
21+
gnustep-2-0 = ["gnustep-1-9", "objc2/gnustep-2-0"]
22+
gnustep-2-1 = ["gnustep-2-0", "objc2/gnustep-2-1"]
23+
24+
# Hack to prevent the feature flag from being enabled in the entire project
25+
assembly-features = ["objc2/unstable-static-class-inlined"]

0 commit comments

Comments
 (0)