1010 lines
35 KiB
Rust
1010 lines
35 KiB
Rust
mod __rewrite_self_arg;
|
|
mod declare_class;
|
|
mod extern_class;
|
|
mod extern_methods;
|
|
|
|
/// Gets a reference to a [`Class`] from the given name.
|
|
///
|
|
/// [`Class`]: crate::runtime::Class
|
|
///
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if no class with the given name can be found.
|
|
///
|
|
/// To check for a class that may not exist, use [`Class::get`].
|
|
///
|
|
/// [`Class::get`]: crate::runtime::Class::get
|
|
///
|
|
///
|
|
/// # Features
|
|
///
|
|
/// If the experimental `"unstable-static-class"` feature is enabled, this
|
|
/// will emit special statics that will be replaced by dyld when the program
|
|
/// starts up.
|
|
///
|
|
/// Errors that were previously runtime panics may now turn into linker errors
|
|
/// if you try to use a class which is not available. Additionally, you may
|
|
/// have to call `msg_send![cls, class]` on the result if you want to use it
|
|
/// in a dynamic context (e.g. dynamically declaring classes).
|
|
///
|
|
/// See the [corresponding section][sel#features] in the [`sel!`] macro for
|
|
/// more details on the limitations of this. The
|
|
/// `"unstable-static-class-inlined"` corresponds to the
|
|
/// `"unstable-static-sel-inlined"` feature here.
|
|
///
|
|
/// [sel#features]: crate::sel#features
|
|
/// [`sel!`]: crate::sel
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```no_run
|
|
/// # use objc2::class;
|
|
/// let cls = class!(NSObject);
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! class {
|
|
($name:ident) => {{
|
|
$crate::__class_inner!($name, $crate::__hash_idents!($name))
|
|
}};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
#[cfg(not(feature = "unstable-static-class"))]
|
|
macro_rules! __class_inner {
|
|
($name:ident, $_hash:expr) => {{
|
|
use $crate::__macro_helpers::{concat, panic, stringify, CachedClass, None, Some};
|
|
static CACHED_CLASS: CachedClass = CachedClass::new();
|
|
let name = concat!(stringify!($name), '\0');
|
|
#[allow(unused_unsafe)]
|
|
let cls = unsafe { CACHED_CLASS.get(name) };
|
|
match cls {
|
|
Some(cls) => cls,
|
|
None => panic!("Class with name {} could not be found", stringify!($name)),
|
|
}
|
|
}};
|
|
}
|
|
|
|
/// Register a selector with the Objective-C runtime.
|
|
///
|
|
/// Returns the [`Sel`] corresponding to the specified selector.
|
|
///
|
|
/// [`Sel`]: crate::runtime::Sel
|
|
///
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if the runtime failed allocating space for the selector.
|
|
///
|
|
///
|
|
/// # Specification
|
|
///
|
|
/// This has similar syntax and functionality as the `@selector` directive in
|
|
/// Objective-C.
|
|
///
|
|
/// This calls [`Sel::register`] internally. The result is cached for
|
|
/// efficiency. The cache for certain common selectors (`alloc`, `init` and
|
|
/// `new`) is deduplicated to reduce code-size.
|
|
///
|
|
/// Non-ascii identifiers are ill-tested, if supported at all.
|
|
///
|
|
/// [`Sel::register`]: crate::runtime::Sel::register
|
|
///
|
|
///
|
|
/// # Features
|
|
///
|
|
/// If the experimental `"unstable-static-sel"` feature is enabled, this will
|
|
/// emit special statics that will be replaced by the dynamic linker (dyld)
|
|
/// when the program starts up - in exactly the same manner as normal
|
|
/// Objective-C code does.
|
|
/// This should be significantly faster (and allow better native debugging),
|
|
/// however due to the Rust compilation model, and since we don't have
|
|
/// low-level control over it, it is currently unlikely that this will work
|
|
/// correctly in all cases.
|
|
/// See the source code and [rust-lang/rust#53929] for more info.
|
|
///
|
|
/// Concretely, this may fail at:
|
|
/// - link-time (likely)
|
|
/// - dynamic link-time/just before the program is run (fairly likely)
|
|
/// - runtime, causing UB (unlikely)
|
|
///
|
|
/// The `"unstable-static-sel-inlined"` feature is the even more extreme
|
|
/// version - it yields the best performance and is closest to real
|
|
/// Objective-C code, but probably won't work unless your code and its
|
|
/// inlining is written in a very certain way.
|
|
///
|
|
/// Enabling LTO greatly increases the chance that these features work.
|
|
///
|
|
/// [rust-lang/rust#53929]: https://github.com/rust-lang/rust/issues/53929
|
|
///
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// Get a few different selectors:
|
|
///
|
|
/// ```rust
|
|
/// use objc2::sel;
|
|
/// let sel = sel!(alloc);
|
|
/// let sel = sel!(description);
|
|
/// let sel = sel!(_privateMethod);
|
|
/// let sel = sel!(storyboardWithName:bundle:);
|
|
/// let sel = sel!(
|
|
/// otherEventWithType:
|
|
/// location:
|
|
/// modifierFlags:
|
|
/// timestamp:
|
|
/// windowNumber:
|
|
/// context:
|
|
/// subtype:
|
|
/// data1:
|
|
/// data2:
|
|
/// );
|
|
/// ```
|
|
///
|
|
/// Whitespace is ignored:
|
|
///
|
|
/// ```
|
|
/// # use objc2::sel;
|
|
/// let sel1 = sel!(setObject:forKey:);
|
|
/// let sel2 = sel!( setObject :
|
|
///
|
|
/// forKey : );
|
|
/// assert_eq!(sel1, sel2);
|
|
/// ```
|
|
///
|
|
/// Invalid selector:
|
|
///
|
|
/// ```compile_fail
|
|
/// # use objc2::sel;
|
|
/// let sel = sel!(aSelector:withoutTrailingColon);
|
|
/// ```
|
|
///
|
|
/// Unsupported usage that you may run into when using macros - fails to
|
|
/// compile when the `"unstable-static-sel"` feature is enabled.
|
|
///
|
|
/// Instead, define a wrapper function that retrieves the selector.
|
|
///
|
|
#[cfg_attr(not(feature = "unstable-static-sel"), doc = "```no_run")]
|
|
#[cfg_attr(feature = "unstable-static-sel", doc = "```compile_fail")]
|
|
/// use objc2::sel;
|
|
/// macro_rules! x {
|
|
/// ($x:ident) => {
|
|
/// // One of these is fine
|
|
/// sel!($x);
|
|
/// // But using the identifier again in the same way is not!
|
|
/// sel!($x);
|
|
/// };
|
|
/// }
|
|
/// // Identifier `abc`
|
|
/// x!(abc);
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! sel {
|
|
(alloc) => ({
|
|
$crate::__macro_helpers::alloc()
|
|
});
|
|
(init) => ({
|
|
$crate::__macro_helpers::init()
|
|
});
|
|
(new) => ({
|
|
$crate::__macro_helpers::new()
|
|
});
|
|
($first:ident $(: $($rest:ident :)*)?) => ({
|
|
use $crate::__macro_helpers::{concat, stringify, str};
|
|
const SELECTOR_DATA: &str = concat!(stringify!($first), $(':', $(stringify!($rest), ':',)*)? '\0');
|
|
$crate::__sel_inner!(SELECTOR_DATA, $crate::__hash_idents!($first $($($rest)*)?))
|
|
});
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
#[cfg(not(feature = "unstable-static-sel"))]
|
|
macro_rules! __sel_inner {
|
|
($data:expr, $_hash:expr) => {{
|
|
use $crate::__macro_helpers::CachedSel;
|
|
static CACHED_SEL: CachedSel = CachedSel::new();
|
|
#[allow(unused_unsafe)]
|
|
unsafe {
|
|
CACHED_SEL.get($data)
|
|
}
|
|
}};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! __inner_statics_apple_generic {
|
|
{
|
|
@string_to_known_length_bytes;
|
|
$x:ident;
|
|
} => {{
|
|
// Convert the `&[u8]` slice to an array with known length, so
|
|
// that we can place that directly in a static.
|
|
let mut res: [u8; $x.len()] = [0; $x.len()];
|
|
let mut i = 0;
|
|
while i < $x.len() {
|
|
res[i] = $x[i];
|
|
i += 1;
|
|
}
|
|
res
|
|
}};
|
|
{
|
|
@image_info;
|
|
$image_info_section:literal;
|
|
$hash:expr;
|
|
} => {
|
|
/// 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.
|
|
///
|
|
/// Unfortunately however, this leads to duplicated tags - the linker
|
|
/// reports `__DATA/__objc_imageinfo has unexpectedly large size XXX`,
|
|
/// but things still seems to work.
|
|
#[link_section = $image_info_section]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_IMAGE_INFO_",
|
|
$hash,
|
|
)]
|
|
#[used] // Make sure this reaches the linker
|
|
static _IMAGE_INFO: $crate::ffi::__ImageInfo = $crate::ffi::__ImageInfo::system();
|
|
};
|
|
{
|
|
@module_info;
|
|
$hash:expr;
|
|
} => {
|
|
#[link_section = "__TEXT,__cstring,cstring_literals"]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_CLASS_NAME_",
|
|
$hash,
|
|
"_MODULE_INFO"
|
|
)]
|
|
static MODULE_INFO_NAME: [$crate::__macro_helpers::u8; 1] = [0];
|
|
|
|
/// Emit module info.
|
|
///
|
|
/// This is similar to image info, and must be present in the final
|
|
/// binary on macOS 32-bit.
|
|
#[link_section = "__OBJC,__module_info,regular,no_dead_strip"]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_MODULES_",
|
|
$hash,
|
|
)]
|
|
#[used] // Make sure this reaches the linker
|
|
static _MODULE_INFO: $crate::__macro_helpers::ModuleInfo = $crate::__macro_helpers::ModuleInfo::new(
|
|
MODULE_INFO_NAME.as_ptr()
|
|
);
|
|
};
|
|
{
|
|
@sel;
|
|
$var_name_section:literal;
|
|
$selector_ref_section:literal;
|
|
$data:expr;
|
|
$hash:expr;
|
|
} => {
|
|
use $crate::__macro_helpers::{u8, UnsafeCell};
|
|
use $crate::runtime::Sel;
|
|
|
|
const X: &[u8] = $data.as_bytes();
|
|
|
|
/// Clang marks this with LLVM's `unnamed_addr`.
|
|
/// See rust-lang/rust#18297
|
|
/// Should only be an optimization (?)
|
|
#[link_section = $var_name_section]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_METH_VAR_NAME_",
|
|
$hash,
|
|
)]
|
|
static NAME_DATA: [u8; X.len()] = $crate::__inner_statics_apple_generic! {
|
|
@string_to_known_length_bytes;
|
|
X;
|
|
};
|
|
|
|
/// Place the constant value in the correct section.
|
|
///
|
|
/// We use `UnsafeCell` because this somewhat resembles internal
|
|
/// mutation - this pointer will be changed by dyld at startup, so we
|
|
/// _must_ prevent Rust/LLVM from trying to "peek inside" it and just
|
|
/// use a pointer to `NAME_DATA` directly.
|
|
///
|
|
/// Clang does this by marking `REF` with LLVM's
|
|
/// `externally_initialized`.
|
|
///
|
|
/// `static mut` is used so that we don't need to wrap the
|
|
/// `UnsafeCell` in something that implements `Sync`.
|
|
///
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// I'm quite uncertain of how safe this is, since the Rust abstract
|
|
/// machine has no concept of a static that is initialized outside of
|
|
/// it - perhaps it would be better to use `read_volatile` instead of
|
|
/// relying on `UnsafeCell`? Or perhaps `MaybeUninit` would help?
|
|
///
|
|
/// See the [`ctor`](https://crates.io/crates/ctor) crate for more
|
|
/// info on "life before main".
|
|
#[link_section = $selector_ref_section]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_SELECTOR_REFERENCES_",
|
|
$hash,
|
|
)]
|
|
static mut REF: UnsafeCell<Sel> = unsafe {
|
|
UnsafeCell::new(Sel::__internal_from_ptr(NAME_DATA.as_ptr().cast()))
|
|
};
|
|
};
|
|
{
|
|
@class;
|
|
$name:ident;
|
|
$hash:expr;
|
|
} => {
|
|
use $crate::__macro_helpers::UnsafeCell;
|
|
use $crate::runtime::Class;
|
|
|
|
extern "C" {
|
|
/// Link to the Objective-C class static.
|
|
///
|
|
/// This uses the special symbol that static and dynamic linkers
|
|
/// knows about.
|
|
///
|
|
/// Failure modes:
|
|
/// - Unknown class: Static linker error.
|
|
/// - OS version < Class introduced version: Dynamic linker error
|
|
/// on program startup.
|
|
/// - Deployment target > Class introduced version: No error,
|
|
/// though _should_ be a static linker error.
|
|
///
|
|
/// Ideally, we'd have some way of allowing this to be weakly
|
|
/// linked, and return `Option<&Class>` in that case, but Rust
|
|
/// doesn't have the capability to do so yet!
|
|
/// <https://github.com/rust-lang/rust/issues/29603>
|
|
/// <https://stackoverflow.com/a/16936512>
|
|
/// <http://sealiesoftware.com/blog/archive/2010/4/8/Do-it-yourself_Objective-C_weak_import.html>
|
|
#[link_name = $crate::__macro_helpers::concat!(
|
|
"OBJC_CLASS_$_",
|
|
$crate::__macro_helpers::stringify!($name),
|
|
)]
|
|
static CLASS: Class;
|
|
}
|
|
|
|
/// SAFETY: Same as `REF` above in `@sel`.
|
|
#[link_section = "__DATA,__objc_classrefs,regular,no_dead_strip"]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_CLASSLIST_REFERENCES_$_",
|
|
$crate::__hash_idents!($name),
|
|
)]
|
|
static mut REF: UnsafeCell<&Class> = unsafe {
|
|
UnsafeCell::new(&CLASS)
|
|
};
|
|
};
|
|
{
|
|
@class_old;
|
|
$name:ident;
|
|
$hash:expr;
|
|
} => {
|
|
use $crate::__macro_helpers::{u8, UnsafeCell};
|
|
use $crate::runtime::Class;
|
|
|
|
const X: &[u8] = $crate::__macro_helpers::stringify!($name).as_bytes();
|
|
|
|
/// Similar to NAME_DATA above in `@sel`.
|
|
#[link_section = "__TEXT,__cstring,cstring_literals"]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_CLASS_NAME_",
|
|
$crate::__hash_idents!($name),
|
|
)]
|
|
static NAME_DATA: [u8; X.len()] = $crate::__inner_statics_apple_generic! {
|
|
@string_to_known_length_bytes;
|
|
X;
|
|
};
|
|
|
|
/// SAFETY: Same as `REF` above in `@sel`.
|
|
#[link_section = "__OBJC,__cls_refs,literal_pointers,no_dead_strip"]
|
|
#[export_name = $crate::__macro_helpers::concat!(
|
|
"\x01L_OBJC_CLASS_REFERENCES_",
|
|
$crate::__hash_idents!($name),
|
|
)]
|
|
static mut REF: UnsafeCell<&Class> = unsafe {
|
|
let ptr: *const Class = NAME_DATA.as_ptr().cast();
|
|
UnsafeCell::new(&*ptr)
|
|
};
|
|
}
|
|
}
|
|
|
|
// 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! __inner_statics {
|
|
(@image_info $hash:expr) => {
|
|
$crate::__inner_statics_apple_generic! {
|
|
@image_info;
|
|
"__DATA,__objc_imageinfo,regular,no_dead_strip";
|
|
$hash;
|
|
}
|
|
};
|
|
(@sel $data:expr, $hash:expr) => {
|
|
$crate::__inner_statics_apple_generic! {
|
|
@sel;
|
|
"__TEXT,__objc_methname,cstring_literals";
|
|
// Clang uses `no_dead_strip` in the link section for some reason,
|
|
// which other tools (notably some LLVM tools) now assume is
|
|
// present, so we have to add it as well.
|
|
"__DATA,__objc_selrefs,literal_pointers,no_dead_strip";
|
|
$data;
|
|
$hash;
|
|
}
|
|
};
|
|
(@class $name:ident, $hash:expr) => {
|
|
$crate::__inner_statics_apple_generic! {
|
|
@class;
|
|
$name;
|
|
$hash;
|
|
}
|
|
};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
#[cfg(all(feature = "apple", target_os = "macos", target_arch = "x86"))]
|
|
macro_rules! __inner_statics {
|
|
(@image_info $hash:expr) => {
|
|
$crate::__inner_statics_apple_generic! {
|
|
@image_info;
|
|
"__OBJC,__image_info,regular";
|
|
$hash;
|
|
}
|
|
};
|
|
(@sel $data:expr, $hash:expr) => {
|
|
$crate::__inner_statics_apple_generic! {
|
|
@sel;
|
|
"__TEXT,__cstring,cstring_literals";
|
|
"__OBJC,__message_refs,literal_pointers,no_dead_strip";
|
|
$data;
|
|
$hash;
|
|
}
|
|
};
|
|
(@class $name:ident, $hash:expr) => {
|
|
$crate::__inner_statics_apple_generic! {
|
|
@class_old;
|
|
$name;
|
|
$hash;
|
|
}
|
|
$crate::__inner_statics_apple_generic! {
|
|
@module_info;
|
|
$hash;
|
|
}
|
|
};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
#[cfg(not(feature = "apple"))]
|
|
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)]
|
|
#[macro_export]
|
|
#[cfg(all(
|
|
feature = "unstable-static-sel",
|
|
not(feature = "unstable-static-sel-inlined")
|
|
))]
|
|
macro_rules! __sel_inner {
|
|
($data:expr, $hash:expr) => {{
|
|
$crate::__inner_statics!(@image_info $hash);
|
|
$crate::__inner_statics!(@sel $data, $hash);
|
|
|
|
/// HACK: Wrap the access in a non-generic, `#[inline(never)]`
|
|
/// function to make the compiler group it into the same codegen unit
|
|
/// as the statics.
|
|
///
|
|
/// See the following link for details on how the compiler decides
|
|
/// to partition code into codegen units:
|
|
/// <https://doc.rust-lang.org/1.61.0/nightly-rustc/rustc_monomorphize/partitioning/index.html>
|
|
#[inline(never)]
|
|
fn objc_static_workaround() -> $crate::runtime::Sel {
|
|
// SAFETY: The actual selector is replaced by dyld when the
|
|
// program is loaded.
|
|
//
|
|
// This is similar to a volatile read, except it can be stripped
|
|
// if unused.
|
|
unsafe { *REF.get() }
|
|
}
|
|
|
|
objc_static_workaround()
|
|
}};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
#[cfg(all(feature = "unstable-static-sel-inlined"))]
|
|
macro_rules! __sel_inner {
|
|
($data:expr, $hash:expr) => {{
|
|
$crate::__inner_statics!(@image_info $hash);
|
|
$crate::__inner_statics!(@sel $data, $hash);
|
|
|
|
#[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, $hash:expr) => {{
|
|
$crate::__inner_statics!(@image_info $hash);
|
|
$crate::__inner_statics!(@class $name, $hash);
|
|
|
|
#[inline(never)]
|
|
fn objc_static_workaround() -> &'static Class {
|
|
// SAFETY: Same as __sel_inner
|
|
unsafe { *REF.get() }
|
|
}
|
|
|
|
objc_static_workaround()
|
|
}};
|
|
}
|
|
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
#[cfg(all(feature = "unstable-static-class-inlined"))]
|
|
macro_rules! __class_inner {
|
|
($name:ident, $hash:expr) => {{
|
|
$crate::__inner_statics!(@image_info $hash);
|
|
$crate::__inner_statics!(@class $name, $hash);
|
|
|
|
#[allow(unused_unsafe)]
|
|
// SAFETY: See above
|
|
unsafe { *REF.get() }
|
|
}};
|
|
}
|
|
|
|
/// Send a message to an object or class.
|
|
///
|
|
/// This is wildly `unsafe`, even more so than sending messages in
|
|
/// Objective-C, because this macro can't inspect header files to see the
|
|
/// expected types, and because Rust has more safety invariants to uphold.
|
|
/// Make sure to review the safety section below!
|
|
///
|
|
/// The recommended way of using this macro is by defining a wrapper function:
|
|
///
|
|
/// ```
|
|
/// # use std::os::raw::{c_int, c_char};
|
|
/// # use objc2::msg_send;
|
|
/// # use objc2::runtime::Object;
|
|
/// unsafe fn do_something(obj: &Object, arg: c_int) -> *const c_char {
|
|
/// msg_send![obj, doSomething: arg]
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// This way we are clearly communicating to Rust that: The method
|
|
/// `doSomething:` works with a shared reference to the object. It takes a
|
|
/// C-style signed integer, and returns a pointer to what is probably a
|
|
/// C-compatible string. Now it's much, _much_ easier to make a safe
|
|
/// abstraction around this!
|
|
///
|
|
/// There exists a variant of this macro, [`msg_send_id!`], which can help
|
|
/// with upholding certain requirements of methods that return Objective-C's
|
|
/// `id`, or other object pointers. Use that whenever you want to call such a
|
|
/// method!
|
|
///
|
|
/// [`msg_send_id!`]: crate::msg_send_id
|
|
///
|
|
///
|
|
/// # Specification
|
|
///
|
|
/// The syntax is similar to the message syntax in Objective-C, except with
|
|
/// an (optional, though consider that deprecated) comma between arguments,
|
|
/// since that works much better with rustfmt.
|
|
///
|
|
/// The first expression, know as the "receiver", can be any type that
|
|
/// implements [`MessageReceiver`], like a reference or a pointer to an
|
|
/// object, or even a reference to an [`rc::Id`] containing an object.
|
|
///
|
|
/// The expression can be wrapped in `super`, with an optional superclass
|
|
/// as the second argument. If no specific superclass is specified, the
|
|
/// direct superclass is retrieved from [`ClassType`].
|
|
///
|
|
/// All arguments, and the return type, must implement [`Encode`].
|
|
///
|
|
/// This macro translates into a call to [`sel!`], and afterwards a fully
|
|
/// qualified call to [`MessageReceiver::send_message`]. Note that this means
|
|
/// that auto-dereferencing of the receiver is not supported, and that the
|
|
/// receiver is consumed. You may encounter a little trouble with `&mut`
|
|
/// references, try refactoring into a separate method or reborrowing the
|
|
/// reference.
|
|
///
|
|
/// Variadic arguments are currently not supported.
|
|
///
|
|
/// [`MessageReceiver`]: crate::MessageReceiver
|
|
/// [`rc::Id`]: crate::rc::Id
|
|
/// [`ClassType`]: crate::ClassType
|
|
/// [`Encode`]: crate::Encode
|
|
/// [`sel!`]: crate::sel
|
|
/// [`MessageReceiver::send_message`]: crate::MessageReceiver::send_message
|
|
///
|
|
///
|
|
/// # `bool` handling
|
|
///
|
|
/// Objective-C's `BOOL` is different from Rust's [`bool`], and hence a
|
|
/// conversion step must be performed before using it. This is _very_ easy to
|
|
/// forget (because it'll happen to work in _most_ cases), so for ease of use,
|
|
/// this macro does the conversion step automatically whenever the argument or
|
|
/// return type is `bool`!
|
|
///
|
|
/// That means that any Objective-C method that take or return `BOOL` can
|
|
/// simply be translated to use `bool` on the Rust side.
|
|
///
|
|
/// If you want to handle the conversion explicitly, or the Objective-C method
|
|
/// expects a pointer to a `BOOL`, use [`runtime::Bool`] instead.
|
|
///
|
|
/// [`runtime::Bool`]: crate::runtime::Bool
|
|
///
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if the `"catch-all"` feature is enabled and the Objective-C method
|
|
/// throws an exception. Exceptions may still cause UB until
|
|
/// `extern "C-unwind"` is stable, see [RFC-2945].
|
|
///
|
|
/// Panics if the `"verify_message"` feature is enabled and the Objective-C
|
|
/// method's argument's encoding does not match the encoding of the given
|
|
/// arguments. This is highly recommended to enable while testing!
|
|
///
|
|
/// [RFC-2945]: https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
|
|
///
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Similar to defining and calling an `extern` function in a foreign function
|
|
/// interface. In particular, you must uphold the following requirements:
|
|
///
|
|
/// 1. The selector corresponds to a valid method that is available on the
|
|
/// receiver.
|
|
///
|
|
/// 2. The argument types match what the receiver excepts for this selector.
|
|
///
|
|
/// 3. The return type match what the receiver returns for this selector.
|
|
///
|
|
/// 4. The call must not violate Rust's mutability rules, for example if
|
|
/// passing an `&T`, the Objective-C method must not mutate the variable
|
|
/// (of course except if the variable is inside [`std::cell::UnsafeCell`]).
|
|
///
|
|
/// 5. If the receiver is a raw pointer it must be valid (aligned,
|
|
/// dereferenceable, initialized and so on). Messages to `null` pointers
|
|
/// are allowed (though heavily discouraged), but _only_ if the return type
|
|
/// itself is a pointer.
|
|
///
|
|
/// 6. The method must not (yet) throw an exception.
|
|
///
|
|
/// 7. You must uphold any additional safety requirements (explicit and
|
|
/// implicit) that the method has. For example:
|
|
/// - Methods that take pointers usually require that the pointer is valid,
|
|
/// and sometimes non-null.
|
|
/// - Sometimes, a method may only be called on the main thread.
|
|
/// - The lifetime of returned pointers usually follows certain rules, and
|
|
/// may not be valid outside of an [`autoreleasepool`] ([`msg_send_id!`]
|
|
/// can greatly help with that).
|
|
///
|
|
/// 8. TODO: Maybe more?
|
|
///
|
|
/// [`autoreleasepool`]: crate::rc::autoreleasepool
|
|
/// [`msg_send_id!`]: crate::msg_send_id
|
|
///
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// Sending messages to an object.
|
|
///
|
|
/// ```no_run
|
|
/// use objc2::msg_send;
|
|
/// use objc2::runtime::Object;
|
|
///
|
|
/// let obj: *mut Object;
|
|
/// # obj = 0 as *mut Object;
|
|
/// let description: *const Object = unsafe { msg_send![obj, description] };
|
|
/// // Usually you'd use msg_send_id here ^
|
|
/// let _: () = unsafe { msg_send![obj, setArg1: 1i32, arg2: true] };
|
|
/// let arg1: i32 = unsafe { msg_send![obj, getArg1] };
|
|
/// let arg2: bool = unsafe { msg_send![obj, getArg2] };
|
|
/// ```
|
|
///
|
|
/// Sending messages to the direct superclass of an object.
|
|
///
|
|
/// ```no_run
|
|
/// use objc2::msg_send;
|
|
/// # use objc2::ns_string;
|
|
/// # use objc2::foundation::{NSString as MyObject};
|
|
///
|
|
/// let obj: &MyObject; // Some object that implements ClassType
|
|
/// # obj = ns_string!("");
|
|
/// let _: () = unsafe { msg_send![super(obj), someMethod] };
|
|
/// ```
|
|
///
|
|
/// Sending messages to a specific superclass of an object.
|
|
///
|
|
/// ```no_run
|
|
/// # use objc2::class;
|
|
/// use objc2::msg_send;
|
|
/// use objc2::runtime::{Class, Object};
|
|
///
|
|
/// // Since we specify the superclass ourselves, this doesn't need to
|
|
/// // implement ClassType
|
|
/// let obj: *mut Object;
|
|
/// # obj = 0 as *mut Object;
|
|
/// let superclass: &Class;
|
|
/// # superclass = class!(NSObject);
|
|
/// let arg3: u32 = unsafe { msg_send![super(obj, superclass), getArg3] };
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! msg_send {
|
|
[super($obj:expr), $selector:ident $(,)?] => ({
|
|
let sel = $crate::sel!($selector);
|
|
let result;
|
|
// Note: `sel` and `result` can be accessed from the `obj` and
|
|
// `superclass` expressions - we won't (yet) bother with preventing
|
|
// that though.
|
|
result = $crate::MessageReceiver::__send_super_message_static($obj, sel, ());
|
|
result
|
|
});
|
|
[super($obj:expr), $($selector:ident : $argument:expr),+ $(,)?] => ({
|
|
let sel = $crate::sel!($($selector :)+);
|
|
let result;
|
|
result = $crate::MessageReceiver::__send_super_message_static($obj, sel, ($($argument,)+));
|
|
result
|
|
});
|
|
[super($obj:expr, $superclass:expr), $selector:ident $(,)?] => ({
|
|
let sel = $crate::sel!($selector);
|
|
let result;
|
|
result = $crate::MessageReceiver::send_super_message($obj, $superclass, sel, ());
|
|
result
|
|
});
|
|
[super($obj:expr, $superclass:expr), $($selector:ident : $argument:expr $(,)?)+] => ({
|
|
let sel = $crate::sel!($($selector :)+);
|
|
let result;
|
|
result = $crate::MessageReceiver::send_super_message($obj, $superclass, sel, ($($argument,)+));
|
|
result
|
|
});
|
|
[$obj:expr, $selector:ident $(,)?] => ({
|
|
let sel = $crate::sel!($selector);
|
|
let result;
|
|
result = $crate::MessageReceiver::send_message($obj, sel, ());
|
|
result
|
|
});
|
|
[$obj:expr, $($selector:ident : $argument:expr $(,)?)+] => ({
|
|
let sel = $crate::sel!($($selector :)+);
|
|
let result;
|
|
result = $crate::MessageReceiver::send_message($obj, sel, ($($argument,)+));
|
|
result
|
|
});
|
|
}
|
|
|
|
/// Deprecated. Use [`msg_send!`] instead.
|
|
#[macro_export]
|
|
#[deprecated = "use a normal msg_send! instead, it will perform the conversion for you"]
|
|
macro_rules! msg_send_bool {
|
|
[$($msg_send_args:tt)+] => ({
|
|
// Use old impl for backwards compat
|
|
let result: $crate::runtime::Bool = $crate::msg_send![$($msg_send_args)+];
|
|
result.as_bool()
|
|
});
|
|
}
|
|
|
|
/// [`msg_send!`] for methods returning `id`, `NSObject*`, or similar object
|
|
/// pointers.
|
|
///
|
|
/// Object pointers in Objective-C have certain rules for when they should be
|
|
/// retained and released across function calls. This macro helps doing that,
|
|
/// and returns an [`rc::Id`] with the object, optionally wrapped in an
|
|
/// [`Option`] if you want to handle failures yourself.
|
|
///
|
|
/// [`rc::Id`]: crate::rc::Id
|
|
///
|
|
///
|
|
/// # A little history
|
|
///
|
|
/// Objective-C's type system is... limited, so you can't easily tell who is
|
|
/// responsible for releasing an object. To remedy this problem, Apple/Cocoa
|
|
/// introduced approximately the following rule:
|
|
///
|
|
/// The caller is responsible for releasing objects return from methods that
|
|
/// begin with `new`, `alloc`, `copy`, `mutableCopy` or `init`, and method
|
|
/// that begins with `init` takes ownership of the receiver. See [Cocoa's
|
|
/// Memory Management Policy][mmRules] for a user-friendly introduction to
|
|
/// this concept.
|
|
///
|
|
/// In the past, users had to do `retain` and `release` calls themselves to
|
|
/// properly follow these rules. To avoid the memory management problems
|
|
/// associated with manual stuff like that, they [introduced "ARC"][arc-rel],
|
|
/// which codifies the rules as part of the language, and inserts the required
|
|
/// `retain` and `release` calls automatically.
|
|
///
|
|
/// [`msg_send!`] is similar to pre-ARC; you have to know when to retain and
|
|
/// when to release an object. [`msg_send_id!`] is similar to ARC; the rules
|
|
/// are simple enough that we can do them automatically!
|
|
///
|
|
/// [mmRules]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html#//apple_ref/doc/uid/20000994-SW1
|
|
/// [arc-rel]: https://developer.apple.com/library/archive/releasenotes/ObjectiveC/RN-TransitioningToARC/Introduction/Introduction.html#//apple_ref/doc/uid/TP40011226
|
|
///
|
|
/// [`msg_send_id!`]: crate::msg_send_id
|
|
///
|
|
///
|
|
/// # Specification
|
|
///
|
|
/// The syntax is the same as in [`msg_send!`].
|
|
///
|
|
/// Attributes like `objc_method_family`, `ns_returns_retained`, `ns_consumed`
|
|
/// and so on must not present on the method - if they are, you should do
|
|
/// manual memory management using the [`msg_send!`] macro instead.
|
|
///
|
|
/// The accepted receiver and return types, and how we handle them, differ
|
|
/// depending on which, if any, of the [recognized selector
|
|
/// families][sel-families] the selector belongs to (here `T: Message` and
|
|
/// `O: Ownership`):
|
|
///
|
|
/// - The `new` family: The receiver must be `&Class`, and the return type
|
|
/// is a generic `Id<T, O>` or `Option<Id<T, O>>`.
|
|
///
|
|
/// - The `alloc` family: The receiver must be `&Class`, and the return type
|
|
/// is a generic `Id<Allocated<T>, O>` or `Option<Id<Allocated<T>, O>>`.
|
|
///
|
|
/// - The `init` family: The receiver must be `Option<Id<Allocated<T>, O>>`
|
|
/// as returned from `alloc`. The receiver is consumed, and a the
|
|
/// now-initialized `Id<T, O>` or `Option<Id<T, O>>` (with the same `T` and
|
|
/// `O`) is returned.
|
|
///
|
|
/// - The `copy` family: The receiver may be anything that implements
|
|
/// [`MessageReceiver`] and the return type is a generic `Id<T, O>` or
|
|
/// `Option<Id<T, O>>`.
|
|
///
|
|
/// - The `mutableCopy` family: Same as the `copy` family.
|
|
///
|
|
/// - No family: The receiver may be anything that implements
|
|
/// [`MessageReceiver`]. The result is retained using
|
|
/// [`Id::retain_autoreleased`], and a generic `Id<T, O>` or
|
|
/// `Option<Id<T, O>>` is returned. This retain is in most cases faster than
|
|
/// using autorelease pools!
|
|
///
|
|
/// See [the clang documentation][arc-retainable] for the precise
|
|
/// specification of Objective-C's ownership rules.
|
|
///
|
|
/// As you may have noticed, the return type is always either `Id<_, _>` or
|
|
/// `Option<Id<_, _>>`. Internally, the return type is always
|
|
/// `Option<Id<_, _>>` (for example: almost all `new` methods can fail if the
|
|
/// allocation failed), but for convenience, if the return type is `Id<_, _>`
|
|
/// this macro will automatically unwrap the object, or panic with an error
|
|
/// message if it couldn't be retrieved.
|
|
///
|
|
/// This macro doesn't support super methods yet, see [#173].
|
|
/// The `retain`, `release` and `autorelease` selectors are not supported, use
|
|
/// [`Id::retain`], [`Id::drop`] and [`Id::autorelease`] for that.
|
|
///
|
|
/// [sel-families]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#arc-method-families
|
|
/// [`MessageReceiver`]: crate::MessageReceiver
|
|
/// [`Id::retain_autoreleased`]: crate::rc::Id::retain_autoreleased
|
|
/// [arc-retainable]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#retainable-object-pointers-as-operands-and-arguments
|
|
/// [#173]: https://github.com/madsmtm/objc2/pull/173
|
|
/// [`Id::retain`]: crate::rc::Id::retain
|
|
/// [`Id::drop`]: crate::rc::Id::drop
|
|
/// [`Id::autorelease`]: crate::rc::Id::autorelease
|
|
///
|
|
///
|
|
/// # Panics
|
|
///
|
|
/// Panics if the return type is specified as `Id<_, _>` and the method
|
|
/// returned NULL.
|
|
///
|
|
/// Additional panicking cases are documented in [`msg_send!`].
|
|
///
|
|
///
|
|
/// # Safety
|
|
///
|
|
/// Same as [`msg_send!`], with an expected return type of `id`,
|
|
/// `instancetype`, `NSObject*`, or other such object pointers. The method
|
|
/// must not have any attributes that changes the how it handles memory
|
|
/// management.
|
|
///
|
|
/// Note that if you're using this inside a context that expects unwinding to
|
|
/// have Objective-C semantics (like [`exception::catch`]), you should make
|
|
/// sure that the return type is `Option<Id<_, _>>` so that you don't get an
|
|
/// unexpected unwind through incompatible ABIs!
|
|
///
|
|
#[cfg_attr(
|
|
feature = "exception",
|
|
doc = "[`exception::catch`]: crate::exception::catch"
|
|
)]
|
|
#[cfg_attr(
|
|
not(feature = "exception"),
|
|
doc = "[`exception::catch`]: crate::exception#feature-not-enabled"
|
|
)]
|
|
///
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```no_run
|
|
/// use objc2::{class, msg_send_id};
|
|
/// use objc2::ffi::NSUInteger;
|
|
/// use objc2::rc::{Id, Shared};
|
|
/// use objc2::runtime::Object;
|
|
// Allocate new object
|
|
/// let obj = unsafe { msg_send_id![class!(NSObject), alloc] };
|
|
/// // Consume the allocated object, return initialized object
|
|
/// let obj: Id<Object, Shared> = unsafe { msg_send_id![obj, init] };
|
|
/// // Copy the object
|
|
/// let copy: Id<Object, Shared> = unsafe { msg_send_id![&obj, copy] };
|
|
/// // Call ordinary selector that returns an object
|
|
/// // This time, we handle failures ourselves
|
|
/// let s: Option<Id<Object, Shared>> = unsafe { msg_send_id![&obj, description] };
|
|
/// let s = s.expect("description was NULL");
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! msg_send_id {
|
|
[$obj:expr, $selector:ident $(,)?] => ({
|
|
$crate::__msg_send_id_helper!(@verify $selector);
|
|
let sel = $crate::sel!($selector);
|
|
const NAME: &[$crate::__macro_helpers::u8] = $crate::__macro_helpers::stringify!($selector).as_bytes();
|
|
$crate::__msg_send_id_helper!(@get_assert_consts NAME);
|
|
let result;
|
|
result = <RS as $crate::__macro_helpers::MsgSendId<_, _, _>>::send_message_id($obj, sel, ());
|
|
result
|
|
});
|
|
[$obj:expr, $($selector:ident : $argument:expr),+ $(,)?] => ({
|
|
let sel = $crate::sel!($($selector:)+);
|
|
const NAME: &[$crate::__macro_helpers::u8] =
|
|
$crate::__macro_helpers::concat!($($crate::__macro_helpers::stringify!($selector), ':'),+).as_bytes();
|
|
$crate::__msg_send_id_helper!(@get_assert_consts NAME);
|
|
let result;
|
|
result = <RS as $crate::__macro_helpers::MsgSendId<_, _, _>>::send_message_id($obj, sel, ($($argument,)+));
|
|
result
|
|
});
|
|
}
|
|
|
|
/// Helper macro to avoid exposing these in the docs for [`msg_send_id!`].
|
|
#[doc(hidden)]
|
|
#[macro_export]
|
|
macro_rules! __msg_send_id_helper {
|
|
(@verify retain) => {{
|
|
$crate::__macro_helpers::compile_error!(
|
|
"msg_send_id![obj, retain] is not supported. Use `Id::retain` instead"
|
|
)
|
|
}};
|
|
(@verify release) => {{
|
|
$crate::__macro_helpers::compile_error!(
|
|
"msg_send_id![obj, release] is not supported. Drop an `Id` instead"
|
|
)
|
|
}};
|
|
(@verify autorelease) => {{
|
|
$crate::__macro_helpers::compile_error!(
|
|
"msg_send_id![obj, autorelease] is not supported. Use `Id::autorelease`"
|
|
)
|
|
}};
|
|
(@verify $selector:ident) => {{}};
|
|
(@get_assert_consts $selector:ident) => {
|
|
use $crate::__macro_helpers::{bool, in_selector_family, RetainSemantics};
|
|
const NEW: bool = in_selector_family($selector, b"new");
|
|
const ALLOC: bool = in_selector_family($selector, b"alloc");
|
|
const INIT: bool = in_selector_family($selector, b"init");
|
|
const COPY_OR_MUT_COPY: bool = {
|
|
in_selector_family($selector, b"copy") || in_selector_family($selector, b"mutableCopy")
|
|
};
|
|
type RS = RetainSemantics<NEW, ALLOC, INIT, COPY_OR_MUT_COPY>;
|
|
};
|
|
}
|