Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor block2 internals #552

Merged
merged 9 commits into from
Jan 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading