Skip to content

Commit

Permalink
Merge pull request #552 from madsmtm/refactor-block-abi
Browse files Browse the repository at this point in the history
Refactor block2 internals
  • Loading branch information
madsmtm authored Jan 7, 2024
2 parents 42bcda9 + 4ecabc6 commit 9a8b29b
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 409 deletions.
483 changes: 246 additions & 237 deletions crates/block2/src/abi.rs

Large diffs are not rendered by default.

26 changes: 19 additions & 7 deletions crates/block2/src/block.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use core::fmt;
use core::marker::PhantomData;
use core::mem;

use objc2::encode::{EncodeArgument, EncodeReturn, Encoding, RefEncode};

use crate::abi;
use crate::abi::BlockHeader;
use crate::debug::debug_block_header;

/// Types that may be used as the arguments of an Objective-C block.
///
Expand Down Expand Up @@ -89,10 +91,10 @@ block_args_impl!(
#[repr(C)]
pub struct Block<A, R> {
_inner: [u8; 0],
// We store `Block_layout` + a bit more, but `Block` has to remain an
// empty type otherwise the compiler thinks we only have provenance over
// `Block_layout`.
_layout: PhantomData<abi::Block_layout>,
// We store `BlockHeader` + the closure captures, but `Block` has to
// remain an empty type otherwise the compiler thinks we only have
// provenance over `BlockHeader`.
_header: PhantomData<BlockHeader>,
// To get correct variance on args and return types
_p: PhantomData<fn(A) -> R>,
}
Expand All @@ -113,10 +115,20 @@ impl<A: BlockArguments, R: EncodeReturn> Block<A, R> {
/// caller must ensure that calling it will not cause a data race.
pub unsafe fn call(&self, args: A) -> R {
let ptr: *const Self = self;
let layout = unsafe { ptr.cast::<abi::Block_layout>().as_ref().unwrap_unchecked() };
let header = unsafe { ptr.cast::<BlockHeader>().as_ref().unwrap_unchecked() };
// TODO: Is `invoke` actually ever null?
let invoke = layout.invoke.unwrap_or_else(|| unreachable!());
let invoke = header.invoke.unwrap_or_else(|| unreachable!());

unsafe { A::__call_block(invoke, ptr as *mut Self, args) }
}
}

impl<A, R> fmt::Debug for Block<A, R> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("Block");
let ptr: *const Self = self;
let header = unsafe { ptr.cast::<BlockHeader>().as_ref().unwrap() };
debug_block_header(header, &mut f);
f.finish_non_exhaustive()
}
}
44 changes: 28 additions & 16 deletions crates/block2/src/concrete_block.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
use core::ffi::c_void;
use core::fmt;
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop};
use core::mem::{self, ManuallyDrop, MaybeUninit};
use core::ops::Deref;
use core::ptr;
use std::os::raw::c_ulong;

use objc2::encode::{EncodeArgument, EncodeReturn, Encoding, RefEncode};

use crate::{abi, ffi, Block, BlockArguments, RcBlock};
use crate::abi::{BlockDescriptorCopyDispose, BlockDescriptorPtr, BlockFlags, BlockHeader};
use crate::debug::debug_block_header;
use crate::{ffi, Block, BlockArguments, RcBlock};

mod private {
pub trait Sealed<A> {}
Expand Down Expand Up @@ -163,7 +166,7 @@ concrete_block_impl!(
#[repr(C)]
pub struct ConcreteBlock<A, R, F> {
p: PhantomData<Block<A, R>>,
pub(crate) layout: abi::Block_layout,
pub(crate) header: BlockHeader,
pub(crate) closure: F,
}

Expand All @@ -187,17 +190,15 @@ where

impl<A, R, F> ConcreteBlock<A, R, F> {
// TODO: Use new ABI with BLOCK_HAS_SIGNATURE
const FLAGS: abi::block_flags = if mem::needs_drop::<Self>() {
abi::BLOCK_HAS_COPY_DISPOSE
const FLAGS: BlockFlags = if mem::needs_drop::<Self>() {
BlockFlags::BLOCK_HAS_COPY_DISPOSE
} else {
0
BlockFlags::EMPTY
};

const DESCRIPTOR: abi::Block_descriptor = abi::Block_descriptor {
header: abi::Block_descriptor_header {
reserved: 0,
size: mem::size_of::<Self>() as c_ulong,
},
const DESCRIPTOR: BlockDescriptorCopyDispose = BlockDescriptorCopyDispose {
reserved: 0,
size: mem::size_of::<Self>() as c_ulong,
copy: if mem::needs_drop::<Self>() {
Some(block_context_copy::<Self>)
} else {
Expand All @@ -214,16 +215,18 @@ impl<A, R, F> ConcreteBlock<A, R, F> {
/// Unsafe because the caller must ensure the invoke function takes the
/// correct arguments.
unsafe fn with_invoke(invoke: unsafe extern "C" fn(), closure: F) -> Self {
let layout = abi::Block_layout {
let header = BlockHeader {
isa: unsafe { ptr::addr_of!(ffi::_NSConcreteStackBlock) },
flags: Self::FLAGS,
reserved: 0,
reserved: MaybeUninit::new(0),
invoke: Some(invoke),
descriptor: &Self::DESCRIPTOR as *const abi::Block_descriptor as *mut c_void,
descriptor: BlockDescriptorPtr {
with_copy_dispose: &Self::DESCRIPTOR,
},
};
Self {
p: PhantomData,
layout,
header,
closure,
}
}
Expand All @@ -243,7 +246,7 @@ impl<A, R, F: 'static> ConcreteBlock<A, R, F> {

impl<A, R, F: Clone> Clone for ConcreteBlock<A, R, F> {
fn clone(&self) -> Self {
unsafe { Self::with_invoke(self.layout.invoke.unwrap(), self.closure.clone()) }
unsafe { Self::with_invoke(self.header.invoke.unwrap(), self.closure.clone()) }
}
}

Expand All @@ -265,3 +268,12 @@ unsafe extern "C" fn block_context_dispose<B>(block: *mut c_void) {
unsafe extern "C" fn block_context_copy<B>(_dst: *mut c_void, _src: *mut c_void) {
// The runtime memmoves the src block into the dst block, nothing to do
}

impl<A, R, F: fmt::Debug> fmt::Debug for ConcreteBlock<A, R, F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("ConcreteBlock");
debug_block_header(&self.header, &mut f);
f.field("closure", &self.closure);
f.finish()
}
}
143 changes: 20 additions & 123 deletions crates/block2/src/debug.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use alloc::format;
use core::ffi::c_void;
use core::fmt::{Debug, DebugStruct, Error, Formatter};
use core::ptr;
use std::ffi::CStr;

use crate::{abi, ffi, Block, ConcreteBlock, GlobalBlock, RcBlock};
use crate::abi::{BlockDescriptorPtr, BlockFlags, BlockHeader};
use crate::ffi;

#[derive(Clone, Copy, PartialEq, Eq)]
struct Isa(*const ffi::Class);
Expand Down Expand Up @@ -34,152 +33,50 @@ impl Debug for Isa {
}
}

fn debug_block_layout(layout: &abi::Block_layout, f: &mut DebugStruct<'_, '_>) {
f.field("isa", &Isa(layout.isa));
f.field("flags", &BlockFlags(layout.flags));
f.field("reserved", &layout.reserved);
f.field("invoke", &layout.invoke);
pub(crate) fn debug_block_header(header: &BlockHeader, f: &mut DebugStruct<'_, '_>) {
f.field("isa", &Isa(header.isa));
f.field("flags", &header.flags);
f.field("reserved", &header.reserved);
f.field("invoke", &header.invoke);
f.field(
"descriptor",
&BlockDescriptor {
has_copy_dispose: layout.flags & abi::BLOCK_HAS_COPY_DISPOSE != 0,
has_signature: layout.flags & abi::BLOCK_HAS_SIGNATURE != 0,
descriptor: layout.descriptor,
&BlockDescriptorHelper {
has_copy_dispose: header.flags.0 & BlockFlags::BLOCK_HAS_COPY_DISPOSE.0 != 0,
has_signature: header.flags.0 & BlockFlags::BLOCK_HAS_SIGNATURE.0 != 0,
descriptor: header.descriptor,
},
);
}

impl<A, R> Debug for Block<A, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let mut f = f.debug_struct("Block");
let ptr: *const Self = self;
let layout = unsafe { ptr.cast::<abi::Block_layout>().as_ref().unwrap() };
debug_block_layout(layout, &mut f);
f.finish_non_exhaustive()
}
}

impl<A, R> Debug for RcBlock<A, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let mut f = f.debug_struct("RcBlock");
let layout = unsafe { self.ptr.cast::<abi::Block_layout>().as_ref().unwrap() };
debug_block_layout(layout, &mut f);
f.finish_non_exhaustive()
}
}

impl<A, R, F: Debug> Debug for ConcreteBlock<A, R, F> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let mut f = f.debug_struct("ConcreteBlock");
debug_block_layout(&self.layout, &mut f);
f.field("closure", &self.closure);
f.finish()
}
}

impl<A, R> Debug for GlobalBlock<A, R> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
let mut f = f.debug_struct("GlobalBlock");
debug_block_layout(&self.layout, &mut f);
f.finish_non_exhaustive()
}
}

#[derive(Clone, Copy, PartialEq, Eq)]
struct BlockFlags(abi::block_flags);

impl Debug for BlockFlags {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
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.0 & abi::$flag != 0));
)*)
}
test_flags! {
#[cfg(feature = "apple")]
deallocating: BLOCK_DEALLOCATING;
#[cfg(feature = "apple")]
inline_layout_string: BLOCK_INLINE_LAYOUT_STRING;
#[cfg(feature = "apple")]
small_descriptor: BLOCK_SMALL_DESCRIPTOR;
#[cfg(feature = "apple")]
is_noescape: BLOCK_IS_NOESCAPE;
#[cfg(feature = "apple")]
needs_free: BLOCK_NEEDS_FREE;
has_copy_dispose: BLOCK_HAS_COPY_DISPOSE;
has_ctor: BLOCK_HAS_CTOR;
#[cfg(feature = "apple")]
is_gc: BLOCK_IS_GC;
is_global: BLOCK_IS_GLOBAL;
use_stret: BLOCK_USE_STRET;
has_signature: BLOCK_HAS_SIGNATURE;
#[cfg(feature = "apple")]
has_extended_layout: BLOCK_HAS_EXTENDED_LAYOUT;
}

f.field(
"over_referenced",
&(self.0 & abi::BLOCK_REFCOUNT_MASK == abi::BLOCK_REFCOUNT_MASK),
);
f.field(
"reference_count",
&((self.0 & abi::BLOCK_REFCOUNT_MASK) >> 1),
);
f.finish_non_exhaustive()
}
}

#[derive(Clone, Copy, PartialEq, Eq)]
struct BlockDescriptor {
#[derive(Clone, Copy)]
struct BlockDescriptorHelper {
has_copy_dispose: bool,
has_signature: bool,
descriptor: *const c_void,
descriptor: BlockDescriptorPtr,
}

impl Debug for BlockDescriptor {
impl Debug for BlockDescriptorHelper {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
if self.descriptor.is_null() {
if unsafe { self.descriptor.basic }.is_null() {
return f.write_str("(null)");
}

let mut f = f.debug_struct("BlockDescriptor");

let header = unsafe {
self.descriptor
.cast::<abi::Block_descriptor_header>()
.as_ref()
.unwrap()
};
let header = unsafe { self.descriptor.basic.as_ref().unwrap() };

f.field("reserved", &header.reserved);
f.field("size", &header.size);

match (self.has_copy_dispose, self.has_signature) {
(false, false) => {}
(true, false) => {
let descriptor = unsafe {
self.descriptor
.cast::<abi::Block_descriptor>()
.as_ref()
.unwrap()
};
let descriptor = unsafe { self.descriptor.with_copy_dispose.as_ref().unwrap() };
f.field("copy", &descriptor.copy);
f.field("dispose", &descriptor.dispose);
}
(false, true) => {
let descriptor = unsafe {
self.descriptor
.cast::<abi::Block_descriptor_basic>()
.as_ref()
.unwrap()
};
let descriptor = unsafe { self.descriptor.with_signature.as_ref().unwrap() };
f.field(
"encoding",
&if descriptor.encoding.is_null() {
Expand All @@ -192,7 +89,7 @@ impl Debug for BlockDescriptor {
(true, true) => {
let descriptor = unsafe {
self.descriptor
.cast::<abi::Block_descriptor_with_signature>()
.with_copy_dispose_signature
.as_ref()
.unwrap()
};
Expand Down
Loading

0 comments on commit 9a8b29b

Please sign in to comment.