Skip to content

Commit

Permalink
Add static classes
Browse files Browse the repository at this point in the history
  • Loading branch information
madsmtm committed Jul 22, 2022
1 parent 2c02ac3 commit f4a6bf3
Show file tree
Hide file tree
Showing 21 changed files with 1,044 additions and 42 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -395,12 +395,12 @@ 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
- name: Test static class and 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
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel,unstable-static-class

- name: Run assembly tests
# Not run on GNUStep yet since a lot of function labels are mangled and
Expand Down
5 changes: 3 additions & 2 deletions objc2-foundation/src/declare_macro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -696,7 +696,7 @@ macro_rules! declare_class {
use $crate::__std::sync::Once;

use $crate::objc2::declare::ClassBuilder;
use $crate::objc2::runtime::Protocol;
use $crate::objc2::runtime::{Class, Protocol};
static REGISTER_CLASS: Once = Once::new();

REGISTER_CLASS.call_once(|| {
Expand Down Expand Up @@ -737,7 +737,8 @@ macro_rules! declare_class {
let _cls = builder.register();
});

$crate::objc2::class!($name)
// We just registered the class, so it should be available
Class::get(stringify!($name)).unwrap()
}
}

Expand Down
4 changes: 4 additions & 0 deletions objc2/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

## Unreleased - YYYY-MM-DD

### Added
* Added the `"unstable-static-class"` and `"unstable-static-class-inlined"`
feature flags to make the `class!` macro zero cost.


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

Expand Down
2 changes: 2 additions & 0 deletions objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ malloc = ["malloc_buf"]
# https://github.com/madsmtm/objc2/issues/new
unstable-static-sel = ["objc2-proc-macros"]
unstable-static-sel-inlined = ["unstable-static-sel"]
unstable-static-class = ["objc2-proc-macros"]
unstable-static-class-inlined = ["unstable-static-class"]

# Uses nightly features to make AutoreleasePool zero-cost even in debug mode
unstable-autoreleasesafe = []
Expand Down
4 changes: 4 additions & 0 deletions objc2/examples/introspection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ use objc2::{class, msg_send, msg_send_id};
#[cfg(feature = "malloc")]
use objc2::{sel, Encode};

#[cfg(feature = "apple")]
#[link(name = "Foundation", kind = "framework")]
extern "C" {}

fn main() {
// Get a class
let cls = class!(NSObject);
Expand Down
3 changes: 3 additions & 0 deletions objc2/examples/talk_to_me.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ use std::ffi::c_void;
#[cfg(feature = "apple")]
#[link(name = "AVFoundation", kind = "framework")]
extern "C" {}
#[cfg(feature = "apple")]
#[link(name = "Foundation", kind = "framework")]
extern "C" {}

const UTF8_ENCODING: NSUInteger = 4;

Expand Down
5 changes: 5 additions & 0 deletions objc2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ mod test_utils;
#[cfg(feature = "malloc")]
mod verify;

// Hack to make doctests work
#[cfg(all(feature = "apple", feature = "unstable-static-class"))]
#[link(name = "Foundation", kind = "framework")]
extern "C" {}

/// Hacky way to make GNUStep link properly to Foundation while testing.
///
/// This is a temporary solution to make our CI work for now!
Expand Down
162 changes: 138 additions & 24 deletions objc2/src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@
/// ```
#[macro_export]
macro_rules! class {
($name:ident) => {{
$crate::__class_inner!($name)
}};
}

#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "unstable-static-class"))]
macro_rules! __class_inner {
($name:ident) => {{
use $crate::__macro_helpers::{concat, panic, stringify, CachedClass, None, Some};
static CACHED_CLASS: CachedClass = CachedClass::new();
Expand All @@ -25,7 +34,7 @@ macro_rules! class {
let cls = unsafe { CACHED_CLASS.get(name) };
match cls {
Some(cls) => cls,
None => panic!("Class with name {} could not be found", stringify!($name)),
None => panic!("Class with name {} could not be found", stringify!($name),),
}
}};
}
Expand Down Expand Up @@ -165,18 +174,12 @@ macro_rules! __sel_inner {

#[doc(hidden)]
#[macro_export]
macro_rules! __sel_inner_statics_apple_generic {
macro_rules! __inner_statics_apple_generic {
{
@image_info;
$image_info_section:literal;
$var_name_section:literal;
$selector_ref_section:literal;
$data:ident,
$($idents:ident)+
} => {
use $crate::__macro_helpers::{__hash_idents, u8, UnsafeCell};
use $crate::ffi::__ImageInfo;
use $crate::runtime::Sel;

/// We always emit the image info tag, since we need it to:
/// - End up in the same codegen unit as the other statics below.
/// - End up in the final binary so it can be read by dyld.
Expand All @@ -185,9 +188,22 @@ macro_rules! __sel_inner_statics_apple_generic {
/// reports `__DATA/__objc_imageinfo has unexpectedly large size XXX`,
/// but things still seems to work.
#[link_section = $image_info_section]
#[export_name = concat!("\x01L_OBJC_IMAGE_INFO_", __hash_idents!($($idents)+))]
#[export_name = $crate::__macro_helpers::concat!(
"\x01L_OBJC_IMAGE_INFO_",
$crate::__macro_helpers::__hash_idents!($($idents)+)
)]
#[used] // Make sure this reaches the linker
static _IMAGE_INFO: __ImageInfo = __ImageInfo::system();
static _IMAGE_INFO: $crate::ffi::__ImageInfo = $crate::ffi::__ImageInfo::system();
};
{
@sel;
$var_name_section:literal;
$selector_ref_section:literal;
$data:ident,
$($idents:ident)+
} => {
use $crate::__macro_helpers::{__hash_idents, u8, UnsafeCell};
use $crate::runtime::Sel;

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

Expand Down Expand Up @@ -241,47 +257,108 @@ macro_rules! __sel_inner_statics_apple_generic {
UnsafeCell::new(Sel::__internal_from_ptr(NAME_DATA.as_ptr().cast()))
};
};
{
@class;
$class_ref_section:literal;
$name:ident
} => {
use $crate::__macro_helpers::{concat, stringify, __hash_idents, UnsafeCell};
use $crate::runtime::Class;

// TODO
extern "C" {
// TODO: Weak linkage?
// https://stackoverflow.com/a/16936512
// http://sealiesoftware.com/blog/archive/2010/4/8/Do-it-yourself_Objective-C_weak_import.html
#[link_name = concat!("OBJC_CLASS_$_", stringify!($name))]
static CLASS: Class;
}

// TODO
#[link_section = $class_ref_section]
#[export_name = concat!("\x01L_OBJC_CLASSLIST_REFERENCES_$_", __hash_idents!($name))]
static mut REF: UnsafeCell<&Class> = unsafe {
UnsafeCell::new(&CLASS)
};
};
}

// These sections are found by reading clang/LLVM sources
#[doc(hidden)]
#[macro_export]
#[cfg(all(feature = "apple", not(all(target_os = "macos", target_arch = "x86"))))]
macro_rules! __sel_inner_statics {
($($args:tt)*) => {
// Found by reading clang/LLVM sources
$crate::__sel_inner_statics_apple_generic! {
macro_rules! __inner_statics {
(@image_info $($args:tt)*) => {
$crate::__inner_statics_apple_generic! {
@image_info;
"__DATA,__objc_imageinfo,regular,no_dead_strip";
$($args)*
}
};
(@sel $($args:tt)*) => {
$crate::__inner_statics_apple_generic! {
@sel;
"__TEXT,__objc_methname,cstring_literals";
"__DATA,__objc_selrefs,literal_pointers,no_dead_strip";
$($args)*
}
};
(@class $($args:tt)*) => {
$crate::__inner_statics_apple_generic! {
@class;
"__DATA,__objc_classrefs,regular,no_dead_strip";
$($args)*
}
};
}

#[doc(hidden)]
#[macro_export]
#[cfg(all(feature = "apple", target_os = "macos", target_arch = "x86"))]
macro_rules! __sel_inner_statics {
($($args:tt)*) => {
$crate::__sel_inner_statics_apple_generic! {
macro_rules! __inner_statics {
(@image_info $($args:tt)*) => {
$crate::__inner_statics_apple_generic! {
@image_info;
"__OBJC,__image_info,regular";
$($args)*
}
};
(@sel $($args:tt)*) => {
$crate::__inner_statics_apple_generic! {
@sel;
"__TEXT,__cstring,cstring_literals";
"__OBJC,__message_refs,literal_pointers,no_dead_strip";
$($args)*
}
};
(@class $($args:tt)*) => {
// TODO
$crate::__macro_helpers::compile_error!(
"The `\"unstable-static-class\"` feature is not yet supported on 32bit macOS!"
)
// TODO: module info
};
}

#[doc(hidden)]
#[macro_export]
#[cfg(not(feature = "apple"))]
macro_rules! __sel_inner_statics {
($($args:tt)*) => {
macro_rules! __inner_statics {
(@image_info $($args:tt)*) => {
// TODO
};
(@sel $($args:tt)*) => {
// TODO
$crate::__macro_helpers::compile_error!(
"The `\"unstable-static-sel\"` feature is not yet supported on GNUStep!"
)
};
(@class $($args:tt)*) => {
// TODO
$crate::__macro_helpers::compile_error!(
"The `\"unstable-static-class\"` feature is not yet supported on GNUStep!"
)
};
}

#[doc(hidden)]
Expand All @@ -291,8 +368,9 @@ macro_rules! __sel_inner_statics {
not(feature = "unstable-static-sel-inlined")
))]
macro_rules! __sel_inner {
($($args:tt)*) => {{
$crate::__sel_inner_statics!($($args)*);
($data:ident, $($idents:ident)+) => {{
$crate::__inner_statics!(@image_info $($idents)+);
$crate::__inner_statics!(@sel $data, $($idents)+);

/// HACK: Wrap the access in a non-generic, `#[inline(never)]`
/// function to make the compiler group it into the same codegen unit
Expand All @@ -319,8 +397,44 @@ macro_rules! __sel_inner {
#[macro_export]
#[cfg(all(feature = "unstable-static-sel-inlined"))]
macro_rules! __sel_inner {
($($args:tt)*) => {{
$crate::__sel_inner_statics!($($args)*);
($data:ident, $($idents:ident)+) => {{
$crate::__inner_statics!(@image_info $($idents)+);
$crate::__inner_statics!(@sel $data, $($idents)+);

#[allow(unused_unsafe)]
// SAFETY: See above
unsafe { *REF.get() }
}};
}

#[doc(hidden)]
#[macro_export]
#[cfg(all(
feature = "unstable-static-class",
not(feature = "unstable-static-class-inlined")
))]
macro_rules! __class_inner {
($name:ident) => {{
$crate::__inner_statics!(@image_info $name);
$crate::__inner_statics!(@class $name);

// SAFETY: Same as __sel_inner
#[inline(never)]
fn objc_static_workaround() -> &'static Class {
unsafe { *REF.get() }
}

objc_static_workaround()
}};
}

#[doc(hidden)]
#[macro_export]
#[cfg(all(feature = "unstable-static-class-inlined"))]
macro_rules! __class_inner {
($name:ident) => {{
$crate::__inner_statics!(@image_info $name);
$crate::__inner_statics!(@class $name);

#[allow(unused_unsafe)]
// SAFETY: See above
Expand Down
5 changes: 3 additions & 2 deletions objc2/src/rc/test_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,11 @@ impl RcTestObject {
builder.add_method(sel!(dealloc), dealloc as unsafe extern "C" fn(_, _));
}

builder.register();
let _cls = builder.register();
});

class!(RcTestObject)
// Can't use `class!` here since `RcTestObject` is dynamically created.
Class::get("RcTestObject").unwrap()
}

pub(crate) fn new() -> Id<Self, Owned> {
Expand Down
7 changes: 4 additions & 3 deletions objc2/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ use std::sync::Once;

use crate::declare::{ClassBuilder, ProtocolBuilder};
use crate::runtime::{Class, Object, Protocol, Sel};
use crate::{class, msg_send, sel};
use crate::{ffi, Encode, Encoding, MessageReceiver};
use crate::{msg_send, sel};

#[derive(Debug)]
pub(crate) struct CustomObject {
Expand Down Expand Up @@ -173,7 +173,8 @@ pub(crate) fn custom_class() -> &'static Class {
builder.register();
});

class!(CustomObject)
// Can't use `class!` here since `CustomObject` is dynamically created.
Class::get("CustomObject").unwrap()
}

pub(crate) fn custom_protocol() -> &'static Protocol {
Expand Down Expand Up @@ -232,7 +233,7 @@ pub(crate) fn custom_subclass() -> &'static Class {
builder.register();
});

class!(CustomSubclassObject)
Class::get("CustomSubclassObject").unwrap()
}

pub(crate) fn custom_subclass_object() -> CustomObject {
Expand Down
Loading

0 comments on commit f4a6bf3

Please sign in to comment.