Source code
Revision control
Copy as Markdown
Other Tools
//! The documentation for these bindings is a mix from GNUStep's and Apple's
//! sources, but the [ABI specification][ABI] is really the place you should
//! be looking!
//!
#![allow(unused)]
use core::ffi::{c_char, c_int, c_ulong, c_void};
use core::fmt;
use core::mem::MaybeUninit;
use core::ops::{BitAnd, BitOr};
use alloc::format;
use crate::ffi::Class;
/// Block descriptor flags.
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct BlockFlags(pub(crate) c_int);
impl BlockFlags {
pub(crate) const EMPTY: Self = Self(0);
/// Note: Not public ABI.
const BLOCK_DEALLOCATING: Self = Self(0x0001);
/// Note: Not public ABI.
const BLOCK_REFCOUNT_MASK: Self = Self(if cfg!(feature = "gnustep-1-7") {
// Mask for the reference count in byref structure's flags field. The low
// 3 bytes are reserved for the reference count, the top byte for the flags.
0x00ffffff
} else if cfg!(any(feature = "compiler-rt", feature = "unstable-objfw")) {
0xffff
} else if cfg!(target_vendor = "apple") {
0xfffe // runtime
} else {
0
});
/// Note: Not public ABI.
const BLOCK_INLINE_LAYOUT_STRING: Self = Self(1 << 21);
/// Note: Not public ABI.
const BLOCK_SMALL_DESCRIPTOR: Self = Self(1 << 22);
pub(crate) const BLOCK_IS_NOESCAPE: Self = Self(1 << 23);
/// Note: Not public ABI.
const BLOCK_NEEDS_FREE: Self = Self(1 << 24);
/// The block descriptor contains copy and dispose helpers.
pub(crate) const BLOCK_HAS_COPY_DISPOSE: Self = Self(1 << 25);
/// Helpers have C++ code.
#[doc(alias = "BLOCK_HAS_CXX_OBJ")]
pub(crate) const BLOCK_HAS_CTOR: Self = Self(1 << 26);
/// Note: Not public ABI.
const BLOCK_IS_GC: Self = Self(1 << 27);
/// Block is stored in global memory and does not need to be copied.
pub(crate) const BLOCK_IS_GLOBAL: Self = Self(1 << 28);
/// Block function uses a calling convention that returns a structure via a
/// pointer passed in by the caller.
///
/// match (BLOCK_USE_STRET, BLOCK_HAS_SIGNATURE) {
/// (false, false) => 10.6.ABI, no signature field available
/// (true, false) => 10.6.ABI, no signature field available
/// (false, true) => ABI.2010.3.16, regular calling convention, presence of signature field
/// (true, true) => ABI.2010.3.16, stret calling convention, presence of signature field,
/// }
///
#[doc(alias = "BLOCK_USE_SRET")]
#[doc(alias = "BLOCK_HAS_DESCRIPTOR")]
pub(crate) const BLOCK_USE_STRET: Self = Self(1 << 29);
/// Block has an Objective-C type encoding.
pub(crate) const BLOCK_HAS_SIGNATURE: Self = Self(1 << 30);
/// Note: Not public ABI.
const BLOCK_HAS_EXTENDED_LAYOUT: Self = Self(1 << 31);
/// `const` version of [`PartialEq`].
pub(crate) const fn equals(self, other: Self) -> bool {
self.0 == other.0
}
/// `const` version of [`BitOr`]: adds the flags together.
pub(crate) const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
/// `const` version of [`BitAnd`]: only keeps the common flags.
pub(crate) const fn intersect(self, other: Self) -> Self {
Self(self.0 & other.0)
}
/// Returns `true` if and only if all the flags from `other` are enabled,
/// i.e. `self & other == other`.
pub(crate) const fn has(self, other: Self) -> bool {
self.intersect(other).equals(other)
}
}
/// See [`BlockFlags::union`].
impl BitOr for BlockFlags {
type Output = Self;
fn bitor(self, other: Self) -> Self {
self.union(other)
}
}
/// See [`BlockFlags::intersect`].
impl BitAnd for BlockFlags {
type Output = Self;
fn bitand(self, other: Self) -> Self {
self.intersect(other)
}
}
impl fmt::Debug for BlockFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("BlockFlags");
f.field("value", &format!("{:032b}", self.0));
macro_rules! test_flags {
{$(
$(#[$m:meta])?
$name:ident: $flag:ident
);* $(;)?} => ($(
$(#[$m])?
f.field(stringify!($name), &self.has(Self::$flag));
)*)
}
test_flags! {
#[cfg(target_vendor = "apple")]
deallocating: BLOCK_DEALLOCATING;
#[cfg(target_vendor = "apple")]
inline_layout_string: BLOCK_INLINE_LAYOUT_STRING;
#[cfg(target_vendor = "apple")]
small_descriptor: BLOCK_SMALL_DESCRIPTOR;
#[cfg(target_vendor = "apple")]
is_noescape: BLOCK_IS_NOESCAPE;
#[cfg(target_vendor = "apple")]
needs_free: BLOCK_NEEDS_FREE;
has_copy_dispose: BLOCK_HAS_COPY_DISPOSE;
has_ctor: BLOCK_HAS_CTOR;
#[cfg(target_vendor = "apple")]
is_gc: BLOCK_IS_GC;
is_global: BLOCK_IS_GLOBAL;
use_stret: BLOCK_USE_STRET;
has_signature: BLOCK_HAS_SIGNATURE;
#[cfg(target_vendor = "apple")]
has_extended_layout: BLOCK_HAS_EXTENDED_LAYOUT;
}
f.field("over_referenced", &self.has(Self::BLOCK_REFCOUNT_MASK));
f.field(
"reference_count",
&((*self & Self::BLOCK_REFCOUNT_MASK).0 >> 1),
);
f.finish_non_exhaustive()
}
}
/// The value is of some id-like type, and should be copied as an Objective-C
/// object: i.e. by sending -retain or via the GC assign functions in GC mode
/// (not yet supported).
///
/// id, NSObject, __attribute__((NSObject)), block, ...
pub(crate) const BLOCK_FIELD_IS_OBJECT: c_int = 3;
/// The field is a block. This must be copied by the block copy functions.
///
/// a block variable
pub(crate) const BLOCK_FIELD_IS_BLOCK: c_int = 7;
/// The field is an indirect reference to a variable declared with the __block
/// storage qualifier.
///
/// the on stack structure holding the __block variable
pub(crate) const BLOCK_FIELD_IS_BYREF: c_int = 8;
/// The field is an indirect reference to a variable declared with the __block
/// storage qualifier.
///
/// declared __weak, only used in byref copy helpers
pub(crate) const BLOCK_FIELD_IS_WEAK: c_int = 16;
/// The field is an indirect reference to a variable declared with the __block
/// storage qualifier.
///
/// called from __block (byref) copy/dispose support routines.
pub(crate) const BLOCK_BYREF_CALLER: c_int = 128;
/// The expected header of every block.
#[repr(C)]
#[doc(alias = "__block_literal")]
#[doc(alias = "Block_layout")]
#[doc(alias = "Block_basic")]
#[allow(missing_debug_implementations)]
#[derive(Clone, Copy)]
pub struct BlockHeader {
/// Class pointer.
///
/// Always initialised to &_NSConcreteStackBlock for blocks that are
/// created on the stack or &_NSConcreteGlobalBlock for blocks that are
/// created in global storage.
pub isa: *const Class,
/// Flags.
///
/// See the `BlockFlags` enumerated type for possible values.
///
/// Contains reference count in Apple's and ObjFW's runtime.
#[doc(alias = "Block_flags")]
pub(crate) flags: BlockFlags,
/// Reserved.
///
/// Initialized to 0 by the compiler, but is said to be uninitialized in
/// the specification.
///
/// Used for the reference count in GNUStep's and WinObjC's runtime.
#[doc(alias = "Block_size")]
pub(crate) reserved: MaybeUninit<c_int>,
/// The function that implements the block.
///
/// The first parameter is a pointer to this structure, the subsequent
/// parameters are the block's explicit parameters.
///
/// If the BLOCK_USE_SRET & BLOCK_HAS_SIGNATURE flag is set, there is an
/// additional hidden parameter, which is a pointer to the space on the
/// stack allocated to hold the return value.
pub invoke: Option<unsafe extern "C-unwind" fn()>,
/// The block's descriptor.
pub(crate) descriptor: BlockDescriptorPtr,
}
/// The type of this is:
/// ```pseudo-code
/// match (BLOCK_HAS_COPY_DISPOSE, BLOCK_HAS_SIGNATURE) {
/// (false, false) => BlockDescriptor,
/// (true, false) => BlockDescriptorCopyDispose,
/// (false, true) => BlockDescriptorSignature,
/// (true, true) => BlockDescriptorCopyDisposeSignature,
/// }
/// ```
///
/// Since all of these start with `BlockDescriptor`, it is always safe to
/// use the `basic` field.
//
// Note: We use an union on top of the pointer, since otherwise the descriptor
// would be forced to have a greater size than is actually required.
#[repr(C)]
#[derive(Clone, Copy)]
pub(crate) union BlockDescriptorPtr {
pub(crate) basic: *const BlockDescriptor,
pub(crate) with_copy_dispose: *const BlockDescriptorCopyDispose,
pub(crate) with_signature: *const BlockDescriptorSignature,
pub(crate) with_copy_dispose_signature: *const BlockDescriptorCopyDisposeSignature,
}
/// Basic block descriptor.
#[repr(C)]
#[doc(alias = "__block_descriptor")]
#[doc(alias = "Block_descriptor_1")]
#[derive(Clone, Copy, Debug)]
pub(crate) struct BlockDescriptor {
/// Reserved for future use. Currently always 0.
pub(crate) reserved: c_ulong,
/// Size of the block.
pub(crate) size: c_ulong,
}
/// Block descriptor that contains copy and dispose operations.
///
/// Requires BLOCK_HAS_COPY_DISPOSE.
#[repr(C)]
#[doc(alias = "__block_descriptor")]
#[doc(alias = "Block_descriptor_2")]
#[derive(Clone, Copy, Debug)]
pub(crate) struct BlockDescriptorCopyDispose {
/// Reserved for future use. Currently always 0.
pub(crate) reserved: c_ulong,
/// Size of the block.
pub(crate) size: c_ulong,
/// Helper to copy the block if it contains nontrivial copy operations.
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) copy: Option<unsafe extern "C-unwind" fn(dst: *mut c_void, src: *const c_void)>,
/// Helper to destroy the block after being copied.
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) dispose: Option<unsafe extern "C-unwind" fn(src: *mut c_void)>,
}
/// Block descriptor that has an encoding / a signature.
///
/// Requires BLOCK_HAS_SIGNATURE.
#[repr(C)]
#[doc(alias = "__block_descriptor")]
#[doc(alias = "Block_descriptor_3")]
#[derive(Clone, Copy, Debug)]
pub(crate) struct BlockDescriptorSignature {
/// Reserved for future use. Currently always 0.
pub(crate) reserved: c_ulong,
/// Size of the block.
pub(crate) size: c_ulong,
/// Objective-C type encoding of the block.
#[doc(alias = "signature")]
pub(crate) encoding: *const c_char,
}
/// Block descriptor that contains copy and dispose operations, and which
/// has an encoding / a signature.
///
/// Requires BLOCK_HAS_COPY_DISPOSE and BLOCK_HAS_SIGNATURE.
#[repr(C)]
#[doc(alias = "__block_descriptor")]
#[doc(alias = "Block_descriptor_2")]
#[doc(alias = "Block_descriptor_3")]
#[derive(Clone, Copy, Debug)]
pub(crate) struct BlockDescriptorCopyDisposeSignature {
/// Reserved for future use. Currently always 0.
pub(crate) reserved: c_ulong,
/// Size of the block.
pub(crate) size: c_ulong,
/// Helper to copy the block if it contains nontrivial copy operations.
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) copy: Option<unsafe extern "C-unwind" fn(dst: *mut c_void, src: *const c_void)>,
/// Helper to destroy the block after being copied.
///
/// This may be NULL since macOS 11.0.1 in Apple's runtime, but this
/// should not be relied on.
pub(crate) dispose: Option<unsafe extern "C-unwind" fn(src: *mut c_void)>,
/// Objective-C type encoding of the block.
#[doc(alias = "signature")]
pub(crate) encoding: *const c_char,
}
#[cfg(test)]
mod tests {
use super::*;
fn assert_no_trailing_padding<T>() {
struct AddU8<T> {
t: T,
extra: u8,
}
assert_ne!(core::mem::size_of::<T>(), core::mem::size_of::<AddU8<T>>());
}
#[test]
fn no_types_have_padding() {
assert_no_trailing_padding::<BlockHeader>();
assert_no_trailing_padding::<BlockDescriptorPtr>();
assert_no_trailing_padding::<BlockDescriptor>();
assert_no_trailing_padding::<BlockDescriptorCopyDispose>();
assert_no_trailing_padding::<BlockDescriptorSignature>();
assert_no_trailing_padding::<BlockDescriptorCopyDisposeSignature>();
}
}