Skip to content

Commit ed4f140

Browse files
authored
Merge pull request #225 from madsmtm/various
Various code cleanups
2 parents a61dd14 + 1cf7ed3 commit ed4f140

File tree

8 files changed

+462
-502
lines changed

8 files changed

+462
-502
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ jobs:
152152
ARGS: --no-default-features --features std --features ${{ matrix.runtime || 'apple' }} ${{ matrix.args }}
153153
# Use --no-fail-fast, except with dinghy
154154
TESTARGS: ${{ matrix.dinghy && ' ' || '--no-fail-fast' }} ${{ matrix.test-args }}
155-
SOME_FEATURES: ${{ matrix.features || 'malloc,block,exception' }}
156-
FEATURES: ${{ matrix.features || 'malloc,block,exception,catch-all,verify_message,uuid' }}
155+
SOME_FEATURES: ${{ matrix.features || 'malloc,block,exception,foundation' }}
156+
FEATURES: ${{ matrix.features || 'malloc,block,exception,foundation,catch-all,verify_message,uuid' }}
157157
UNSTABLE_FEATURES: ${{ matrix.unstable-features || 'unstable-autoreleasesafe,unstable-c-unwind' }}
158158

159159
runs-on: ${{ matrix.os }}
@@ -403,7 +403,7 @@ jobs:
403403
uses: actions-rs/cargo@v1
404404
with:
405405
command: test
406-
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features unstable-static-sel,unstable-static-class
406+
args: ${{ env.ARGS }} ${{ env.TESTARGS }} --features foundation,unstable-static-sel,unstable-static-class
407407

408408
- name: Run assembly tests
409409
if: ${{ !contains(matrix.runtime, 'compiler-rt') }}

block2/src/block.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
use core::marker::PhantomData;
2+
use core::mem;
3+
4+
use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode};
5+
6+
use crate::ffi;
7+
8+
/// Types that may be used as the arguments to an Objective-C block.
9+
pub trait BlockArguments: Sized {
10+
/// Calls the given `Block` with self as the arguments.
11+
///
12+
/// # Safety
13+
///
14+
/// The given block must point to a valid `Block`.
15+
///
16+
/// This invokes foreign code whose safety the user must guarantee.
17+
unsafe fn call_block<R>(self, block: *mut Block<Self, R>) -> R;
18+
}
19+
20+
macro_rules! block_args_impl {
21+
($($a:ident : $t:ident),*) => (
22+
impl<$($t),*> BlockArguments for ($($t,)*) {
23+
unsafe fn call_block<R>(self, block: *mut Block<Self, R>) -> R {
24+
let layout = unsafe { block.cast::<ffi::Block_layout>().as_ref().unwrap_unchecked() };
25+
// TODO: Can `invoke` actually be null?
26+
let invoke: unsafe extern "C" fn() = layout.invoke.unwrap();
27+
let invoke: unsafe extern "C" fn(*mut Block<Self, R>, $($t),*) -> R =
28+
unsafe { mem::transmute(invoke) }
29+
;
30+
let ($($a,)*) = self;
31+
unsafe { invoke(block, $($a),*) }
32+
}
33+
}
34+
);
35+
}
36+
37+
block_args_impl!();
38+
block_args_impl!(a: A);
39+
block_args_impl!(a: A, b: B);
40+
block_args_impl!(a: A, b: B, c: C);
41+
block_args_impl!(a: A, b: B, c: C, d: D);
42+
block_args_impl!(a: A, b: B, c: C, d: D, e: E);
43+
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F);
44+
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
45+
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
46+
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
47+
block_args_impl!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
48+
block_args_impl!(
49+
a: A,
50+
b: B,
51+
c: C,
52+
d: D,
53+
e: E,
54+
f: F,
55+
g: G,
56+
h: H,
57+
i: I,
58+
j: J,
59+
k: K
60+
);
61+
block_args_impl!(
62+
a: A,
63+
b: B,
64+
c: C,
65+
d: D,
66+
e: E,
67+
f: F,
68+
g: G,
69+
h: H,
70+
i: I,
71+
j: J,
72+
k: K,
73+
l: L
74+
);
75+
76+
/// An Objective-C block that takes arguments of `A` when called and
77+
/// returns a value of `R`.
78+
#[repr(C)]
79+
pub struct Block<A, R> {
80+
_inner: [u8; 0],
81+
// We effectively store `Block_layout` + a bit more, but `Block` has to
82+
// remain an empty type otherwise the compiler thinks we only have
83+
// provenance over `Block_layout`.
84+
_layout: PhantomData<ffi::Block_layout>,
85+
// To get correct variance on args and return types
86+
_p: PhantomData<fn(A) -> R>,
87+
}
88+
89+
unsafe impl<A: BlockArguments + EncodeArguments, R: Encode> RefEncode for Block<A, R> {
90+
const ENCODING_REF: Encoding<'static> = Encoding::Block;
91+
}
92+
93+
impl<A: BlockArguments + EncodeArguments, R: Encode> Block<A, R> {
94+
/// Call self with the given arguments.
95+
///
96+
/// # Safety
97+
///
98+
/// This invokes foreign code that the caller must verify doesn't violate
99+
/// any of Rust's safety rules.
100+
///
101+
/// For example, if this block is shared with multiple references, the
102+
/// caller must ensure that calling it will not cause a data race.
103+
pub unsafe fn call(&self, args: A) -> R {
104+
unsafe { args.call_block(self as *const Self as *mut Self) }
105+
}
106+
}

block2/src/concrete_block.rs

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,238 @@
1+
use core::ffi::c_void;
2+
use core::marker::PhantomData;
3+
use core::mem::{self, ManuallyDrop};
4+
use core::ops::Deref;
5+
use core::ptr;
6+
use std::os::raw::c_ulong;
7+
8+
use objc2_encode::{Encode, EncodeArguments, Encoding, RefEncode};
9+
10+
use crate::{ffi, Block, BlockArguments, RcBlock};
11+
12+
/// Types that may be converted into a `ConcreteBlock`.
13+
pub trait IntoConcreteBlock<A: BlockArguments + EncodeArguments>: Sized {
14+
/// The return type of the resulting `ConcreteBlock`.
15+
type Ret: Encode;
16+
17+
/// Consumes self to create a `ConcreteBlock`.
18+
fn into_concrete_block(self) -> ConcreteBlock<A, Self::Ret, Self>;
19+
}
20+
21+
macro_rules! concrete_block_impl {
22+
($f:ident) => (
23+
concrete_block_impl!($f,);
24+
);
25+
($f:ident, $($a:ident : $t:ident),*) => (
26+
impl<$($t: Encode,)* R: Encode, X> IntoConcreteBlock<($($t,)*)> for X
27+
where
28+
X: Fn($($t,)*) -> R,
29+
{
30+
type Ret = R;
31+
32+
fn into_concrete_block(self) -> ConcreteBlock<($($t,)*), R, X> {
33+
extern "C" fn $f<$($t,)* R, X>(
34+
block: &ConcreteBlock<($($t,)*), R, X>,
35+
$($a: $t,)*
36+
) -> R
37+
where
38+
X: Fn($($t,)*) -> R,
39+
{
40+
(block.closure)($($a),*)
41+
}
42+
43+
let f: extern "C" fn(&ConcreteBlock<($($t,)*), R, X>, $($a: $t,)*) -> R = $f;
44+
let f: unsafe extern "C" fn() = unsafe { mem::transmute(f) };
45+
unsafe { ConcreteBlock::with_invoke(f, self) }
46+
}
47+
}
48+
);
49+
}
50+
51+
concrete_block_impl!(concrete_block_invoke_args0);
52+
concrete_block_impl!(concrete_block_invoke_args1, a: A);
53+
concrete_block_impl!(concrete_block_invoke_args2, a: A, b: B);
54+
concrete_block_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
55+
concrete_block_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
56+
concrete_block_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
57+
concrete_block_impl!(
58+
concrete_block_invoke_args6,
59+
a: A,
60+
b: B,
61+
c: C,
62+
d: D,
63+
e: E,
64+
f: F
65+
);
66+
concrete_block_impl!(
67+
concrete_block_invoke_args7,
68+
a: A,
69+
b: B,
70+
c: C,
71+
d: D,
72+
e: E,
73+
f: F,
74+
g: G
75+
);
76+
concrete_block_impl!(
77+
concrete_block_invoke_args8,
78+
a: A,
79+
b: B,
80+
c: C,
81+
d: D,
82+
e: E,
83+
f: F,
84+
g: G,
85+
h: H
86+
);
87+
concrete_block_impl!(
88+
concrete_block_invoke_args9,
89+
a: A,
90+
b: B,
91+
c: C,
92+
d: D,
93+
e: E,
94+
f: F,
95+
g: G,
96+
h: H,
97+
i: I
98+
);
99+
concrete_block_impl!(
100+
concrete_block_invoke_args10,
101+
a: A,
102+
b: B,
103+
c: C,
104+
d: D,
105+
e: E,
106+
f: F,
107+
g: G,
108+
h: H,
109+
i: I,
110+
j: J
111+
);
112+
concrete_block_impl!(
113+
concrete_block_invoke_args11,
114+
a: A,
115+
b: B,
116+
c: C,
117+
d: D,
118+
e: E,
119+
f: F,
120+
g: G,
121+
h: H,
122+
i: I,
123+
j: J,
124+
k: K
125+
);
126+
concrete_block_impl!(
127+
concrete_block_invoke_args12,
128+
a: A,
129+
b: B,
130+
c: C,
131+
d: D,
132+
e: E,
133+
f: F,
134+
g: G,
135+
h: H,
136+
i: I,
137+
j: J,
138+
k: K,
139+
l: L
140+
);
141+
142+
/// An Objective-C block whose size is known at compile time and may be
143+
/// constructed on the stack.
144+
#[repr(C)]
145+
pub struct ConcreteBlock<A, R, F> {
146+
p: PhantomData<Block<A, R>>,
147+
pub(crate) layout: ffi::Block_layout,
148+
pub(crate) closure: F,
149+
}
150+
151+
unsafe impl<A: BlockArguments + EncodeArguments, R: Encode, F> RefEncode
152+
for ConcreteBlock<A, R, F>
153+
{
154+
const ENCODING_REF: Encoding<'static> = Encoding::Block;
155+
}
156+
157+
impl<A, R, F> ConcreteBlock<A, R, F>
158+
where
159+
A: BlockArguments + EncodeArguments,
160+
R: Encode,
161+
F: IntoConcreteBlock<A, Ret = R>,
162+
{
163+
/// Constructs a `ConcreteBlock` with the given closure.
164+
/// When the block is called, it will return the value that results from
165+
/// calling the closure.
166+
pub fn new(closure: F) -> Self {
167+
closure.into_concrete_block()
168+
}
169+
}
170+
171+
impl<A, R, F> ConcreteBlock<A, R, F> {
172+
// TODO: Use new ABI with BLOCK_HAS_SIGNATURE
173+
const FLAGS: ffi::block_flags = ffi::BLOCK_HAS_COPY_DISPOSE;
174+
175+
const DESCRIPTOR: ffi::Block_descriptor = ffi::Block_descriptor {
176+
header: ffi::Block_descriptor_header {
177+
reserved: 0,
178+
size: mem::size_of::<Self>() as c_ulong,
179+
},
180+
copy: Some(block_context_copy::<Self>),
181+
dispose: Some(block_context_dispose::<Self>),
182+
};
183+
184+
/// Constructs a `ConcreteBlock` with the given invoke function and closure.
185+
/// Unsafe because the caller must ensure the invoke function takes the
186+
/// correct arguments.
187+
unsafe fn with_invoke(invoke: unsafe extern "C" fn(), closure: F) -> Self {
188+
let layout = ffi::Block_layout {
189+
isa: unsafe { &ffi::_NSConcreteStackBlock },
190+
flags: Self::FLAGS,
191+
reserved: 0,
192+
invoke: Some(invoke),
193+
descriptor: &Self::DESCRIPTOR as *const ffi::Block_descriptor as *mut c_void,
194+
};
195+
Self {
196+
p: PhantomData,
197+
layout,
198+
closure,
199+
}
200+
}
201+
}
202+
203+
impl<A, R, F: 'static> ConcreteBlock<A, R, F> {
204+
/// Copy self onto the heap as an `RcBlock`.
205+
pub fn copy(self) -> RcBlock<A, R> {
206+
// Our copy helper will run so the block will be moved to the heap
207+
// and we can forget the original block because the heap block will
208+
// drop in our dispose helper. TODO: Verify this.
209+
let mut block = ManuallyDrop::new(self);
210+
let ptr: *mut Self = &mut *block;
211+
unsafe { RcBlock::copy(ptr.cast()) }
212+
}
213+
}
214+
215+
impl<A, R, F: Clone> Clone for ConcreteBlock<A, R, F> {
216+
fn clone(&self) -> Self {
217+
unsafe { Self::with_invoke(self.layout.invoke.unwrap(), self.closure.clone()) }
218+
}
219+
}
220+
221+
impl<A, R, F> Deref for ConcreteBlock<A, R, F> {
222+
type Target = Block<A, R>;
223+
224+
fn deref(&self) -> &Self::Target {
225+
let ptr: *const Self = self;
226+
let ptr: *const Block<A, R> = ptr.cast();
227+
// TODO: SAFETY
228+
unsafe { ptr.as_ref().unwrap_unchecked() }
229+
}
230+
}
231+
232+
unsafe extern "C" fn block_context_dispose<B>(block: *mut c_void) {
233+
unsafe { ptr::drop_in_place(block.cast::<B>()) };
234+
}
235+
236+
unsafe extern "C" fn block_context_copy<B>(_dst: *mut c_void, _src: *mut c_void) {
237+
// The runtime memmoves the src block into the dst block, nothing to do
238+
}

0 commit comments

Comments
 (0)