Skip to content

Commit be73526

Browse files
committed
Add static classes
1 parent 98e111c commit be73526

19 files changed

Lines changed: 1036 additions & 39 deletions

File tree

.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/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
@@ -223,6 +223,11 @@ mod test_utils;
223223
#[doc(hidden)]
224224
pub mod __macro_helpers;
225225

226+
// Hack to make doctests work
227+
#[cfg(all(feature = "apple", feature = "unstable-static-class"))]
228+
#[link(name = "Foundation", kind = "framework")]
229+
extern "C" {}
230+
226231
/// Hacky way to make GNUStep link properly to Foundation while testing.
227232
///
228233
/// 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
@@ -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 {

objc2/tests/id_retain_autoreleased.rs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ use objc2::rc::{autoreleasepool, Id, Shared};
44
use objc2::runtime::Object;
55
use objc2::{class, msg_send};
66

7+
#[cfg(feature = "gnustep-1-7")]
8+
#[test]
9+
fn ensure_linkage() {
10+
unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() };
11+
}
12+
13+
#[cfg(feature = "apple")]
14+
#[link(name = "Foundation", kind = "framework")]
15+
extern "C" {}
16+
717
fn retain_count(obj: &Object) -> usize {
818
unsafe { msg_send![obj, retainCount] }
919
}
@@ -37,15 +47,6 @@ fn create_data(bytes: &[u8]) -> Id<Object, Shared> {
3747

3848
#[test]
3949
fn test_retain_autoreleased() {
40-
#[cfg(feature = "gnustep-1-7")]
41-
unsafe {
42-
objc2::__gnustep_hack::get_class_to_force_linkage()
43-
};
44-
45-
#[cfg(feature = "apple")]
46-
#[link(name = "Foundation", kind = "framework")]
47-
extern "C" {}
48-
4950
autoreleasepool(|_| {
5051
// Run once to allow DYLD to resolve the symbol stubs.
5152
// Required for making `retain_autoreleased` work on x86_64.

objc2/tests/no_prelude.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,16 @@
99

1010
extern crate objc2 as new_objc2;
1111

12+
#[cfg(feature = "gnustep-1-7")]
13+
#[test]
14+
fn ensure_linkage() {
15+
unsafe { new_objc2::__gnustep_hack::get_class_to_force_linkage() };
16+
}
17+
18+
#[cfg(feature = "apple")]
19+
#[link(name = "Foundation", kind = "framework")]
20+
extern "C" {}
21+
1222
mod core {}
1323
mod std {}
1424
mod libc {}

0 commit comments

Comments
 (0)