use core::ffi::c_void;
use core::marker::PhantomData;
use core::mem::{self, ManuallyDrop};
use core::ops::Deref;
use core::ptr;
use std::os::raw::c_ulong;
use objc2::encode::{EncodeArgument, EncodeReturn, Encoding, RefEncode};
use crate::{ffi, Block, BlockArguments, RcBlock};
mod private {
pub trait Sealed<A> {}
}
pub unsafe trait IntoConcreteBlock<A: BlockArguments>: private::Sealed<A> + Sized {
type Output: EncodeReturn;
#[doc(hidden)]
fn __into_concrete_block(self) -> ConcreteBlock<A, Self::Output, Self>;
}
macro_rules! concrete_block_impl {
($f:ident) => (
concrete_block_impl!($f,);
);
($f:ident, $($a:ident : $t:ident),*) => (
impl<$($t: EncodeArgument,)* R: EncodeReturn, X> private::Sealed<($($t,)*)> for X
where
X: Fn($($t,)*) -> R,
{}
unsafe impl<$($t: EncodeArgument,)* R: EncodeReturn, X> IntoConcreteBlock<($($t,)*)> for X
where
X: Fn($($t,)*) -> R,
{
type Output = R;
fn __into_concrete_block(self) -> ConcreteBlock<($($t,)*), R, X> {
extern "C" fn $f<$($t,)* R, X>(
block: &ConcreteBlock<($($t,)*), R, X>,
$($a: $t,)*
) -> R
where
X: Fn($($t,)*) -> R,
{
(block.closure)($($a),*)
}
let f: extern "C" fn(&ConcreteBlock<($($t,)*), R, X>, $($a: $t,)*) -> R = $f;
let f: unsafe extern "C" fn() = unsafe { mem::transmute(f) };
unsafe { ConcreteBlock::with_invoke(f, self) }
}
}
);
}
concrete_block_impl!(concrete_block_invoke_args0);
concrete_block_impl!(concrete_block_invoke_args1, a: A);
concrete_block_impl!(concrete_block_invoke_args2, a: A, b: B);
concrete_block_impl!(concrete_block_invoke_args3, a: A, b: B, c: C);
concrete_block_impl!(concrete_block_invoke_args4, a: A, b: B, c: C, d: D);
concrete_block_impl!(concrete_block_invoke_args5, a: A, b: B, c: C, d: D, e: E);
concrete_block_impl!(
concrete_block_invoke_args6,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F
);
concrete_block_impl!(
concrete_block_invoke_args7,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G
);
concrete_block_impl!(
concrete_block_invoke_args8,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H
);
concrete_block_impl!(
concrete_block_invoke_args9,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I
);
concrete_block_impl!(
concrete_block_invoke_args10,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J
);
concrete_block_impl!(
concrete_block_invoke_args11,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J,
k: K
);
concrete_block_impl!(
concrete_block_invoke_args12,
a: A,
b: B,
c: C,
d: D,
e: E,
f: F,
g: G,
h: H,
i: I,
j: J,
k: K,
l: L
);
#[repr(C)]
pub struct ConcreteBlock<A, R, F> {
p: PhantomData<Block<A, R>>,
pub(crate) layout: ffi::Block_layout,
pub(crate) closure: F,
}
unsafe impl<A: BlockArguments, R: EncodeReturn, F> RefEncode for ConcreteBlock<A, R, F> {
const ENCODING_REF: Encoding = Encoding::Block;
}
impl<A, R, F> ConcreteBlock<A, R, F>
where
A: BlockArguments,
R: EncodeReturn,
F: IntoConcreteBlock<A, Output = R>,
{
pub fn new(closure: F) -> Self {
closure.__into_concrete_block()
}
}
impl<A, R, F> ConcreteBlock<A, R, F> {
const FLAGS: ffi::block_flags = if mem::needs_drop::<Self>() {
ffi::BLOCK_HAS_COPY_DISPOSE
} else {
0
};
const DESCRIPTOR: ffi::Block_descriptor = ffi::Block_descriptor {
header: ffi::Block_descriptor_header {
reserved: 0,
size: mem::size_of::<Self>() as c_ulong,
},
copy: if mem::needs_drop::<Self>() {
Some(block_context_copy::<Self>)
} else {
None
},
dispose: if mem::needs_drop::<Self>() {
Some(block_context_dispose::<Self>)
} else {
None
},
};
unsafe fn with_invoke(invoke: unsafe extern "C" fn(), closure: F) -> Self {
let layout = ffi::Block_layout {
isa: unsafe { &ffi::_NSConcreteStackBlock },
flags: Self::FLAGS,
reserved: 0,
invoke: Some(invoke),
descriptor: &Self::DESCRIPTOR as *const ffi::Block_descriptor as *mut c_void,
};
Self {
p: PhantomData,
layout,
closure,
}
}
}
impl<A, R, F: 'static> ConcreteBlock<A, R, F> {
pub fn copy(self) -> RcBlock<A, R> {
let mut block = ManuallyDrop::new(self);
let ptr: *mut Self = &mut *block;
unsafe { RcBlock::copy(ptr.cast()) }
}
}
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()) }
}
}
impl<A, R, F> Deref for ConcreteBlock<A, R, F> {
type Target = Block<A, R>;
fn deref(&self) -> &Self::Target {
let ptr: *const Self = self;
let ptr: *const Block<A, R> = ptr.cast();
unsafe { ptr.as_ref().unwrap_unchecked() }
}
}
unsafe extern "C" fn block_context_dispose<B>(block: *mut c_void) {
unsafe { ptr::drop_in_place(block.cast::<B>()) };
}
unsafe extern "C" fn block_context_copy<B>(_dst: *mut c_void, _src: *mut c_void) {
}