1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
use core::fmt;
use core::marker::PhantomData;
use core::ptr::NonNull;
use objc2::encode::{Encoding, RefEncode};
use crate::abi::BlockHeader;
use crate::debug::debug_block_header;
use crate::rc_block::block_copy_fail;
use crate::{BlockFn, RcBlock};
/// An opaque type that holds an Objective-C block.
///
/// The generic type `F` must be a [`dyn`] [`Fn`] that implements
/// the [`BlockFn`] trait (which means parameter and return types must be
/// "encodable"), and describes the parameter and return types of the block.
///
/// For example, you may have the type `Block<dyn Fn(u8, u8) -> i32>`, and
/// that would be a `'static` block that takes two `u8`s, and returns an
/// `i32`.
///
/// If you want the block to carry a lifetime, use `Block<dyn Fn() + 'a>`,
/// just like you'd usually do with `dyn Fn`.
///
/// [`dyn`]: https://doc.rust-lang.org/std/keyword.dyn.html
///
///
/// # Memory layout
///
/// This is intented to be an `extern type`, and as such the memory layout of
/// this type is _not_ guaranteed. That said, **pointers** to this type are
/// always thin, and match that of Objective-C blocks. So the layout of e.g.
/// `&Block<dyn Fn(...) -> ... + '_>` is defined, and guaranteed to be
/// pointer-sized and ABI-compatible with a block pointer.
///
///
/// # Safety invariant
///
/// Calling this potentially invokes foreign code, so you must verify, when
/// creating a reference to this, or returning it from an external API, that
/// it doesn't violate any of Rust's safety rules.
///
/// In particular, blocks are sharable with multiple references (see e.g.
/// [`Block::copy`]), so the caller must ensure that calling it can never
/// cause a data race. This usually means you'll have to use some form of
/// interior mutability, if you need to mutate something from inside a block.
//
// TODO: Potentially restrict to `F: BlockFn`, for better error messages?
#[repr(C)]
pub struct Block<F: ?Sized> {
_inner: [u8; 0],
/// We store `BlockHeader` + the closure captures, but `Block` has to
/// remain an empty type because we don't know the size of the closure,
/// and otherwise the compiler would think we only have provenance over
/// `BlockHeader`.
///
/// This is possible to improve once we have extern types.
_header: PhantomData<BlockHeader>,
_p: PhantomData<F>,
}
// SAFETY: Pointers to `Block` is an Objective-C block.
// This is only valid when `F: BlockFn`, as that bounds the parameters and
// return type to be encodable too.
unsafe impl<F: ?Sized + BlockFn> RefEncode for Block<F> {
const ENCODING_REF: Encoding = Encoding::Block;
}
impl<F: ?Sized> Block<F> {
fn header(&self) -> &BlockHeader {
let ptr: NonNull<Self> = NonNull::from(self);
let ptr: NonNull<BlockHeader> = ptr.cast();
// SAFETY: `Block` is `BlockHeader` + closure
unsafe { ptr.as_ref() }
}
/// Copy the block onto the heap as an [`RcBlock`].
///
/// The behaviour of this function depends on whether the block is from a
/// [`RcBlock`] or a [`StackBlock`]. In the former case, it will bump the
/// reference-count (just as-if you'd `Clone`'d the `RcBlock`), in the
/// latter case it will construct a new `RcBlock` from the `StackBlock`.
///
/// This distiction should not matter, except for micro-optimizations.
///
/// [`StackBlock`]: crate::StackBlock
#[doc(alias = "Block_copy")]
#[doc(alias = "_Block_copy")]
#[inline]
pub fn copy(&self) -> RcBlock<F> {
let ptr: *const Self = self;
let ptr: *mut Block<F> = ptr as *mut _;
// SAFETY: The lifetime of the block is extended from `&self` to that
// of the `RcBlock`, which is fine, because the lifetime of the
// contained closure `F` is still carried along to the `RcBlock`.
unsafe { RcBlock::copy(ptr) }.unwrap_or_else(|| block_copy_fail())
}
/// Call the block.
///
/// The arguments must be passed as a tuple. The return is the output of
/// the block.
#[doc(alias = "invoke")]
pub fn call(&self, args: F::Args) -> F::Output
where
F: BlockFn,
{
// TODO: Is `invoke` actually ever null?
let invoke = self.header().invoke.unwrap_or_else(|| unreachable!());
let ptr: NonNull<Self> = NonNull::from(self);
let ptr: *mut Self = ptr.as_ptr();
// SAFETY: The closure is an `Fn`, and as such is safe to call from an
// immutable reference.
unsafe { F::__call_block(invoke, ptr, args) }
}
}
impl<F: ?Sized> fmt::Debug for Block<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("Block");
debug_block_header(self.header(), &mut f);
f.finish_non_exhaustive()
}
}
#[cfg(test)]
mod tests {
use core::cell::Cell;
use core::sync::atomic::{AtomicUsize, Ordering};
use super::*;
/// Test that the way you specify lifetimes are as documented in the
/// reference.
/// <https://doc.rust-lang.org/nightly/reference/lifetime-elision.html#default-trait-object-lifetimes>
#[test]
fn test_rust_dyn_lifetime_semantics() {
fn takes_static(block: &Block<dyn Fn() + 'static>) {
block.call(());
}
fn takes_elided(block: &Block<dyn Fn() + '_>) {
block.call(());
}
fn takes_unspecified(block: &Block<dyn Fn()>) {
block.call(());
}
// Static lifetime
static MY_STATIC: AtomicUsize = AtomicUsize::new(0);
MY_STATIC.store(0, Ordering::Relaxed);
let static_lifetime: RcBlock<dyn Fn() + 'static> = RcBlock::new(|| {
MY_STATIC.fetch_add(1, Ordering::Relaxed);
});
takes_static(&static_lifetime);
takes_elided(&static_lifetime);
takes_unspecified(&static_lifetime);
assert_eq!(MY_STATIC.load(Ordering::Relaxed), 3);
// Lifetime declared with `'_`
let captured = Cell::new(0);
let elided_lifetime: RcBlock<dyn Fn() + '_> = RcBlock::new(|| {
captured.set(captured.get() + 1);
});
// takes_static(&elided_lifetime); // Compile error
takes_elided(&elided_lifetime);
// takes_unspecified(&elided_lifetime); // Compile error
assert_eq!(captured.get(), 1);
// Lifetime kept unspecified
let captured = Cell::new(0);
let unspecified_lifetime: RcBlock<dyn Fn()> = RcBlock::new(|| {
captured.set(captured.get() + 1);
});
// takes_static(&unspecified_lifetime); // Compile error
takes_elided(&unspecified_lifetime);
// takes_unspecified(&unspecified_lifetime); // Compile error
assert_eq!(captured.get(), 1);
}
#[allow(dead_code)]
fn unspecified_in_fn_is_static(block: &Block<dyn Fn()>) -> &Block<dyn Fn() + 'static> {
block
}
#[allow(dead_code)]
fn lending_block<'b>(block: &Block<dyn Fn() -> &'b i32 + 'b>) {
let _ = *block.call(());
}
#[allow(dead_code)]
fn takes_lifetime(_: &Block<dyn Fn(&i32) -> &i32>) {
// Not actually callable yet
}
#[allow(dead_code)]
fn covariant<'b, 'f>(b: &'b Block<dyn Fn() + 'static>) -> &'b Block<dyn Fn() + 'f> {
b
}
}