Skip to content

Commit 363d538

Browse files
committed
Improve block2 soundness
Remove DerefMut impl for ConcreteBlock which is kinda useless until we support FnMut. Clean up types, both `invoke` and `block_context_dispose`.
1 parent 04919d1 commit 363d538

File tree

3 files changed

+56
-67
lines changed

3 files changed

+56
-67
lines changed

block2/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
1414
* Improved documentation.
1515
* **BREAKING**: Updated `ffi` module to `block-sys v0.0.3`
1616

17+
### Removed
18+
* **BREAKING**: Removed `DerefMut` implementation for `ConcreteBlock`.
19+
1720

1821
## 0.2.0-alpha.2 - 2021-12-22
1922

block2/src/global.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ where
8282
{
8383
type Target = Block<A, R>;
8484

85-
fn deref(&self) -> &Block<A, R> {
85+
fn deref(&self) -> &Self::Target {
8686
// TODO: SAFETY
8787
unsafe { &*(self as *const Self as *const Block<A, R>) }
8888
}

block2/src/lib.rs

Lines changed: 52 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -89,11 +89,12 @@ extern crate std;
8989
#[doc = include_str!("../README.md")]
9090
extern "C" {}
9191

92+
use core::ffi::c_void;
9293
use core::marker::PhantomData;
9394
use core::mem::{self, ManuallyDrop};
94-
use core::ops::{Deref, DerefMut};
95+
use core::ops::Deref;
9596
use core::ptr;
96-
use std::os::raw::{c_int, c_ulong};
97+
use std::os::raw::c_ulong;
9798

9899
pub use block_sys as ffi;
99100
use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode};
@@ -119,12 +120,14 @@ macro_rules! block_args_impl {
119120
($($a:ident : $t:ident),*) => (
120121
impl<$($t),*> BlockArguments for ($($t,)*) {
121122
unsafe fn call_block<R>(self, block: *mut Block<Self, R>) -> R {
122-
let invoke: unsafe extern "C" fn(*mut Block<Self, R> $(, $t)*) -> R = {
123-
let base = block as *mut BlockBase<Self, R>;
124-
unsafe { mem::transmute((*base).invoke) }
125-
};
123+
let layout = unsafe { block.cast::<ffi::Block_layout>().as_ref().unwrap_unchecked() };
124+
// TODO: Can `invoke` actually be null?
125+
let invoke: unsafe extern "C" fn() = layout.invoke.unwrap();
126+
let invoke: unsafe extern "C" fn(*mut Block<Self, R>, $($t),*) -> R =
127+
unsafe { mem::transmute(invoke) }
128+
;
126129
let ($($a,)*) = self;
127-
unsafe { invoke(block $(, $a)*) }
130+
unsafe { invoke(block, $($a),*) }
128131
}
129132
}
130133
);
@@ -169,19 +172,12 @@ block_args_impl!(
169172
l: L
170173
);
171174

172-
#[repr(C)]
173-
struct BlockBase<A, R> {
174-
isa: *const ffi::Class,
175-
flags: c_int,
176-
_reserved: c_int,
177-
invoke: unsafe extern "C" fn(*mut Block<A, R>, ...) -> R,
178-
}
179-
180175
/// An Objective-C block that takes arguments of `A` when called and
181176
/// returns a value of `R`.
182177
#[repr(C)]
183178
pub struct Block<A, R> {
184-
_base: PhantomData<BlockBase<A, R>>,
179+
_inner: [u8; 0],
180+
p: PhantomData<(ffi::Block_layout, fn(A) -> R)>,
185181
}
186182

187183
unsafe impl<A: BlockArguments + EncodeArguments, R: Encode> RefEncode for Block<A, R> {
@@ -199,7 +195,7 @@ impl<A: BlockArguments + EncodeArguments, R: Encode> Block<A, R> {
199195
/// For example, if this block is shared with multiple references, the
200196
/// caller must ensure that calling it will not cause a data race.
201197
pub unsafe fn call(&self, args: A) -> R {
202-
unsafe { args.call_block(self as *const _ as *mut _) }
198+
unsafe { args.call_block(self as *const Self as *mut Self) }
203199
}
204200
}
205201

@@ -249,7 +245,7 @@ impl<A, R> Deref for RcBlock<A, R> {
249245

250246
fn deref(&self) -> &Block<A, R> {
251247
// SAFETY: The pointer is ensured valid by creator functions.
252-
unsafe { &*self.ptr }
248+
unsafe { self.ptr.as_ref().unwrap_unchecked() }
253249
}
254250
}
255251

@@ -280,21 +276,19 @@ macro_rules! concrete_block_impl {
280276
type Ret = R;
281277

282278
fn into_concrete_block(self) -> ConcreteBlock<($($t,)*), R, X> {
283-
unsafe extern fn $f<$($t,)* R, X>(
284-
block_ptr: *mut ConcreteBlock<($($t,)*), R, X>
285-
$(, $a: $t)*
279+
extern "C" fn $f<$($t,)* R, X>(
280+
block: &ConcreteBlock<($($t,)*), R, X>,
281+
$($a: $t,)*
286282
) -> R
287283
where
288-
X: Fn($($t,)*) -> R
284+
X: Fn($($t,)*) -> R,
289285
{
290-
let block = unsafe { &*block_ptr };
291286
(block.closure)($($a),*)
292287
}
293288

294-
let f: unsafe extern "C" fn(*mut ConcreteBlock<($($t,)*), R, X> $(, $a: $t)*) -> R = $f;
295-
unsafe {
296-
ConcreteBlock::with_invoke(mem::transmute(f), self)
297-
}
289+
let f: extern "C" fn(&ConcreteBlock<($($t,)*), R, X>, $($a: $t,)*) -> R = $f;
290+
let f: unsafe extern "C" fn() = unsafe { mem::transmute(f) };
291+
unsafe { ConcreteBlock::with_invoke(f, self) }
298292
}
299293
}
300294
);
@@ -395,8 +389,8 @@ concrete_block_impl!(
395389
/// constructed on the stack.
396390
#[repr(C)]
397391
pub struct ConcreteBlock<A, R, F> {
398-
base: BlockBase<A, R>,
399-
descriptor: *const BlockDescriptor<ConcreteBlock<A, R, F>>,
392+
p: PhantomData<Block<A, R>>,
393+
layout: ffi::Block_layout,
400394
closure: F,
401395
}
402396

@@ -421,25 +415,32 @@ where
421415
}
422416

423417
impl<A, R, F> ConcreteBlock<A, R, F> {
424-
const DESCRIPTOR: BlockDescriptor<Self> = BlockDescriptor {
425-
_reserved: 0,
426-
block_size: mem::size_of::<Self>() as c_ulong,
427-
copy_helper: block_context_copy::<Self>,
428-
dispose_helper: block_context_dispose::<Self>,
418+
// TODO: Use new ABI with BLOCK_HAS_SIGNATURE
419+
const FLAGS: ffi::block_flags = ffi::BLOCK_HAS_COPY_DISPOSE;
420+
421+
const DESCRIPTOR: ffi::Block_descriptor = ffi::Block_descriptor {
422+
header: ffi::Block_descriptor_header {
423+
reserved: 0,
424+
size: mem::size_of::<Self>() as c_ulong,
425+
},
426+
copy: Some(block_context_copy::<Self>),
427+
dispose: Some(block_context_dispose::<Self>),
429428
};
430429

431430
/// Constructs a `ConcreteBlock` with the given invoke function and closure.
432431
/// Unsafe because the caller must ensure the invoke function takes the
433432
/// correct arguments.
434-
unsafe fn with_invoke(invoke: unsafe extern "C" fn(*mut Self, ...) -> R, closure: F) -> Self {
435-
ConcreteBlock {
436-
base: BlockBase {
437-
isa: unsafe { &ffi::_NSConcreteStackBlock },
438-
flags: ffi::BLOCK_HAS_COPY_DISPOSE,
439-
_reserved: 0,
440-
invoke: unsafe { mem::transmute(invoke) },
441-
},
442-
descriptor: &Self::DESCRIPTOR,
433+
unsafe fn with_invoke(invoke: unsafe extern "C" fn(), closure: F) -> Self {
434+
let layout = ffi::Block_layout {
435+
isa: unsafe { &ffi::_NSConcreteStackBlock },
436+
flags: Self::FLAGS,
437+
reserved: 0,
438+
invoke: Some(invoke),
439+
descriptor: &Self::DESCRIPTOR as *const ffi::Block_descriptor as *mut c_void,
440+
};
441+
Self {
442+
p: PhantomData,
443+
layout,
443444
closure,
444445
}
445446
}
@@ -452,48 +453,33 @@ impl<A, R, F: 'static> ConcreteBlock<A, R, F> {
452453
// and we can forget the original block because the heap block will
453454
// drop in our dispose helper. TODO: Verify this.
454455
let mut block = ManuallyDrop::new(self);
455-
unsafe { RcBlock::copy(&mut **block) }
456+
let ptr: *mut Self = &mut *block;
457+
unsafe { RcBlock::copy(ptr.cast()) }
456458
}
457459
}
458460

459461
impl<A, R, F: Clone> Clone for ConcreteBlock<A, R, F> {
460462
fn clone(&self) -> Self {
461-
unsafe {
462-
ConcreteBlock::with_invoke(mem::transmute(self.base.invoke), self.closure.clone())
463-
}
463+
unsafe { Self::with_invoke(self.layout.invoke.unwrap(), self.closure.clone()) }
464464
}
465465
}
466466

467467
impl<A, R, F> Deref for ConcreteBlock<A, R, F> {
468468
type Target = Block<A, R>;
469469

470-
fn deref(&self) -> &Block<A, R> {
471-
unsafe { &*(&self.base as *const _ as *const Block<A, R>) }
472-
}
473-
}
474-
475-
impl<A, R, F> DerefMut for ConcreteBlock<A, R, F> {
476-
fn deref_mut(&mut self) -> &mut Block<A, R> {
477-
unsafe { &mut *(&mut self.base as *mut _ as *mut Block<A, R>) }
470+
fn deref(&self) -> &Self::Target {
471+
unsafe { &*(self as *const Self as *const Block<A, R>) }
478472
}
479473
}
480474

481-
unsafe extern "C" fn block_context_dispose<B>(block: &mut B) {
482-
unsafe { ptr::drop_in_place(block) };
475+
unsafe extern "C" fn block_context_dispose<B>(block: *mut c_void) {
476+
unsafe { ptr::drop_in_place(block.cast::<B>()) };
483477
}
484478

485-
unsafe extern "C" fn block_context_copy<B>(_dst: &mut B, _src: &B) {
479+
unsafe extern "C" fn block_context_copy<B>(_dst: *mut c_void, _src: *mut c_void) {
486480
// The runtime memmoves the src block into the dst block, nothing to do
487481
}
488482

489-
#[repr(C)]
490-
struct BlockDescriptor<B> {
491-
_reserved: c_ulong,
492-
block_size: c_ulong,
493-
copy_helper: unsafe extern "C" fn(&mut B, &B),
494-
dispose_helper: unsafe extern "C" fn(&mut B),
495-
}
496-
497483
#[cfg(test)]
498484
mod tests {
499485
// Tests live in top level `tests` helper crate

0 commit comments

Comments
 (0)