//! 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! //! //! [ABI]: https://clang.llvm.org/docs/Block-ABI-Apple.html #![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, /// } /// /// See #[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, /// 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, /// 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, /// 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, } /// 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, /// 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, /// 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() { struct AddU8 { t: T, extra: u8, } assert_ne!(core::mem::size_of::(), core::mem::size_of::>()); } #[test] fn no_types_have_padding() { assert_no_trailing_padding::(); assert_no_trailing_padding::(); assert_no_trailing_padding::(); assert_no_trailing_padding::(); assert_no_trailing_padding::(); assert_no_trailing_padding::(); } }