use alloc::boxed::Box;
use core::{marker::PhantomData, mem::ManuallyDrop};
#[cfg(feature = "proc_macros")]
#[doc(hidden)]
pub use closure_ffi_proc_macros::bare_hrtb as bare_hrtb_impl;
#[cfg(feature = "global_jit_alloc")]
use crate::jit_alloc::GlobalJitAlloc;
#[allow(unused_imports)]
use crate::{
arch::AllocatedThunk,
cc,
jit_alloc::{JitAlloc, JitAllocError},
traits::{Any, FnMutThunk, FnOnceThunk, FnPtr, FnThunk, ToBoxedDyn},
};
#[cfg(not(feature = "coverage"))]
#[doc(hidden)]
#[macro_export]
macro_rules! bare_hrtb_inner {
($($tokens:tt)*) => {
$crate::bare_closure::bare_hrtb_impl! { $crate, $($tokens)* }
};
}
#[cfg(feature = "coverage")]
#[doc(hidden)]
#[macro_export]
macro_rules! bare_hrtb_inner {
($($tokens:tt)*) => {
$crate::bare_closure::bare_hrtb_impl! { #[coverage(off)] $crate, $($tokens)* }
};
}
#[cfg(feature = "proc_macros")]
#[macro_export]
macro_rules! bare_hrtb {
($($tokens:tt)*) => {
$crate::bare_hrtb_inner! { $($tokens)* }
};
}
#[cfg(feature = "proc_macros")]
pub use bare_hrtb;
#[allow(unused_macros)]
macro_rules! cc_shorthand {
($fn_name:ident, $trait_ident:ident, $cc_ty:ty, $cc_name:literal $(,$cfg:meta)?) => {
$(#[cfg(any($cfg, doc))])?
#[doc = "Create a bare function thunk using the "]
#[doc = $cc_name]
#[doc = "calling convention for `fun`."]
#[inline]
pub fn $fn_name<F>(fun: F) -> Self
where
F: ToBoxedDyn<S>,
($cc_ty, F): $trait_ident<B>,
{
Self::with_cc(<$cc_ty>::default(), fun)
}
};
}
macro_rules! cc_shorthand_in {
($fn_name:ident, $trait_ident:ident, $cc_ty:ty, $cc_name:literal $(,$cfg:meta)?) => {
$(#[cfg(any($cfg, doc))])?
#[doc = "Create a bare function thunk using the "]
#[doc = $cc_name]
#[doc = "calling convention for `fun`."]
#[inline]
pub fn $fn_name<F>(fun: F, jit_alloc: A) -> Self
where
F: ToBoxedDyn<S>,
($cc_ty, F): $trait_ident<B>,
{
Self::with_cc_in(<$cc_ty>::default(), fun, jit_alloc)
}
};
}
macro_rules! bare_closure_impl {
(
ty_name: $ty_name:ident,
erased_ty_name: $erased_ty_name:ident,
non_sync_alias: $non_sync_alias:ident,
sync_alias: $sync_alias:ident,
sync_bounds: ($($sync_bounds: path),*),
sync_alias_bound: $sync_alias_bound: ty,
trait_ident: $trait_ident:ident,
thunk_template: $thunk_template:ident,
bare_toggle: $bare_toggle:meta,
bare_receiver: $bare_receiver:ty,
fn_trait_doc: $fn_trait_doc:literal,
ty_name_doc: $ty_name_doc:literal,
with_cc_doc: $with_cc_doc:literal,
with_cc_in_doc: $with_cc_in_doc:literal,
try_with_cc_in_doc: $try_with_cc_in_doc:literal,
non_sync_alias_doc: $non_sync_alias_doc:literal,
sync_alias_doc: $sync_alias_doc:literal,
sync_alias_bound_doc: $sync_alias_bound_doc:literal,
safety_doc: $safety_doc:literal
) => {
#[cfg(feature = "global_jit_alloc")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[doc = $fn_trait_doc]
#[doc = $ty_name_doc]
#[doc = $ty_name_doc]
#[allow(dead_code)]
pub struct $erased_ty_name<S: ?Sized, A: JitAlloc = GlobalJitAlloc> {
thunk: AllocatedThunk<A>,
storage: *mut S,
}
#[cfg(not(feature = "global_jit_alloc"))]
#[doc = $fn_trait_doc]
#[doc = $ty_name_doc]
#[doc = $ty_name_doc]
#[allow(dead_code)]
pub struct $erased_ty_name<S: ?Sized, A: JitAlloc> {
thunk: AllocatedThunk<A>,
storage: *mut S,
}
unsafe impl<S: ?Sized + Send, A: JitAlloc + Send> Send for $erased_ty_name<S, A> {}
unsafe impl<S: $($sync_bounds+)* ?Sized, A: JitAlloc> Sync for $erased_ty_name<S, A> {}
impl<S: ?Sized, A: JitAlloc> $erased_ty_name<S, A> {
#[$bare_toggle]
#[doc = $safety_doc]
#[inline]
pub fn bare(self: $bare_receiver) -> *const () {
self.thunk.thunk_ptr()
}
#[doc = $safety_doc]
#[inline]
pub fn leak(self) -> *const ()
where
Self: 'static,
{
ManuallyDrop::new(self).thunk.thunk_ptr()
}
pub fn upcast<U: ?Sized>(self) -> $erased_ty_name<U, A>
where PhantomData<S>: ToBoxedDyn<U>
{
unsafe { core::mem::transmute_copy(&ManuallyDrop::new(self)) }
}
}
impl<B: FnPtr, S: ?Sized, U: ?Sized, A: JitAlloc> From<$ty_name<B, S, A>> for $erased_ty_name<U, A>
where PhantomData<S>: ToBoxedDyn<U>
{
fn from(value: $ty_name<B, S, A>) -> Self {
value.into_untyped().upcast()
}
}
impl<S: ?Sized, A: JitAlloc> Drop for $erased_ty_name<S, A> {
fn drop(&mut self) {
drop(unsafe { Box::from_raw(self.storage) })
}
}
#[cfg(feature = "global_jit_alloc")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[doc = $fn_trait_doc]
#[doc = $non_sync_alias_doc]
#[doc = $sync_alias_doc]
#[doc = $sync_alias_bound_doc]
#[allow(dead_code)]
#[repr(transparent)]
pub struct $ty_name<B: FnPtr, S: ?Sized, A: JitAlloc = GlobalJitAlloc> {
untyped: $erased_ty_name<S, A>,
phantom: PhantomData<B>,
}
#[cfg(not(feature = "global_jit_alloc"))]
#[doc = $fn_trait_doc]
#[doc = $non_sync_alias_doc]
#[doc = $sync_alias_doc]
#[doc = $sync_alias_bound_doc]
#[allow(dead_code)]
pub struct $ty_name<B: FnPtr, S: ?Sized, A: JitAlloc> {
untyped: $erased_ty_name<S, A>,
phantom: PhantomData<B>,
}
#[cfg(feature = "global_jit_alloc")]
impl<B: FnPtr, S: ?Sized> $ty_name<B, S, GlobalJitAlloc> {
#[doc = $with_cc_doc]
#[inline]
pub fn new<F>(fun: F) -> Self
where
F: ToBoxedDyn<S>,
(B::CC, F): $trait_ident<B>,
{
Self::new_in(fun, Default::default())
}
#[inline]
pub fn with_cc<CC, F>(cconv: CC, fun: F) -> Self
where
F: ToBoxedDyn<S>,
(CC, F): $trait_ident<B>,
{
Self::with_cc_in(cconv, fun, Default::default())
}
#[doc = $ty_name_doc]
#[doc = concat!("[`", stringify!($trait_ident), "`].")]
pub fn with_thunk<T>(thunk: T) -> Self where T: $trait_ident<B> + ToBoxedDyn<S>
{
Self::with_thunk_in(thunk, Default::default())
}
}
impl<B: FnPtr, S: ?Sized, A: JitAlloc> $ty_name<B, S, A> {
#[$bare_toggle]
#[doc = $safety_doc]
#[inline]
pub fn bare(self: $bare_receiver) -> B {
unsafe { B::from_ptr(self.untyped.bare()) }
}
#[doc = $safety_doc]
#[inline]
pub fn leak(self) -> B
where
Self: 'static,
{
unsafe { B::from_ptr(self.untyped.leak()) }
}
#[doc = $ty_name_doc]
pub fn into_untyped(self) -> $erased_ty_name<S, A> {
self.untyped
}
pub fn upcast<U: ?Sized>(self) -> $ty_name<B, U, A>
where PhantomData<S>: ToBoxedDyn<U>,
{
unsafe { core::mem::transmute_copy(&ManuallyDrop::new(self)) }
}
#[allow(unused_variables)]
pub fn try_with_cc_in<CC, F>(
cconv: CC,
fun: F,
jit_alloc: A,
) -> Result<Self, JitAllocError>
where
F: ToBoxedDyn<S>,
(CC, F): $trait_ident<B>,
{
let storage = Box::into_raw(F::to_boxed_unsize(fun));
let thunk = unsafe {
AllocatedThunk::new(
<(CC, F)>::$thunk_template,
storage as *const _, size_of::<F>(),
jit_alloc
)?
};
Ok(Self {
untyped: $erased_ty_name {
thunk,
storage
},
phantom: PhantomData,
})
}
#[doc = $try_with_cc_in_doc]
#[allow(unused_variables)]
#[inline]
pub fn with_cc_in<CC, F>(cconv: CC, fun: F, jit_alloc: A) -> Self
where
F: ToBoxedDyn<S>,
(CC, F): $trait_ident<B>,
{
Self::try_with_cc_in(cconv, fun, jit_alloc).unwrap()
}
#[doc = $with_cc_in_doc]
#[doc = $try_with_cc_in_doc]
#[inline]
pub fn new_in<F>(fun: F, jit_alloc: A) -> Self
where
F: ToBoxedDyn<S>,
(B::CC, F): $trait_ident<B>,
{
Self::with_cc_in(B::CC::default(), fun, jit_alloc)
}
#[doc = $ty_name_doc]
#[doc = concat!("[`", stringify!($trait_ident), "`].")]
pub fn try_with_thunk_in<T>(thunk: T, jit_alloc: A) -> Result<Self, JitAllocError>
where T: $trait_ident<B> + ToBoxedDyn<S>
{
let storage = Box::into_raw(T::to_boxed_unsize(thunk));
let thunk = unsafe {
AllocatedThunk::new(
T::$thunk_template,
storage as *const _, size_of::<T>(),
jit_alloc
)?
};
Ok(Self {
untyped: $erased_ty_name {
thunk,
storage
},
phantom: PhantomData,
})
}
#[doc = $ty_name_doc]
#[doc = concat!("[`", stringify!($trait_ident), "`].")]
#[inline]
pub fn with_thunk_in<T>(thunk: T, jit_alloc: A) -> Self
where T: $trait_ident<B> + ToBoxedDyn<S>
{
Self::try_with_thunk_in(thunk, jit_alloc).unwrap()
}
}
#[cfg(feature = "global_jit_alloc")]
impl<B: FnPtr, S: ?Sized> $ty_name<B, S, GlobalJitAlloc> {
cc_shorthand!(new_rust, $trait_ident, cc::Rust, "Rust");
cc_shorthand!(new_c, $trait_ident, cc::C, "C");
cc_shorthand!(new_system, $trait_ident, cc::System, "system");
cc_shorthand!(new_efiabi, $trait_ident, cc::Efiapi, "efiapi");
cc_shorthand!(
new_sysv64,
$trait_ident,
cc::Sysv64,
"sysv64",
all(not(windows), target_arch = "x86_64")
);
cc_shorthand!(
new_aapcs,
$trait_ident,
cc::Aapcs,
"aapcs",
any(doc, target_arch = "arm")
);
cc_shorthand!(
new_fastcall,
$trait_ident,
cc::Fastcall,
"fastcall",
all(windows, target_arch = "x86")
);
cc_shorthand!(
new_stdcall,
$trait_ident,
cc::Stdcall,
"stdcall",
all(windows, target_arch = "x86")
);
cc_shorthand!(
new_cdecl,
$trait_ident,
cc::Cdecl,
"cdecl",
all(windows, target_arch = "x86")
);
cc_shorthand!(
new_thiscall,
$trait_ident,
cc::Thiscall,
"thiscall",
all(windows, target_arch = "x86")
);
cc_shorthand!(
new_win64,
$trait_ident,
cc::Win64,
"win64",
all(windows, target_arch = "x86_64")
);
cc_shorthand!(
new_variadic,
$trait_ident,
cc::Variadic,
"`C` variadic",
feature = "c_variadic"
);
}
impl<B: FnPtr, S: ?Sized, A: JitAlloc> $ty_name<B, S, A> {
cc_shorthand_in!(new_rust_in, $trait_ident, cc::Rust, "Rust");
cc_shorthand_in!(new_c_in, $trait_ident, cc::C, "C");
cc_shorthand_in!(new_system_in, $trait_ident, cc::System, "system");
cc_shorthand_in!(new_efiabi_in, $trait_ident, cc::Efiapi, "efiapi");
cc_shorthand_in!(
new_sysv64_in,
$trait_ident,
cc::Sysv64,
"sysv64",
all(not(windows), target_arch = "x86_64")
);
cc_shorthand_in!(
new_aapcs_in,
$trait_ident,
cc::Aapcs,
"aapcs",
any(doc, target_arch = "arm")
);
cc_shorthand_in!(
new_fastcall_in,
$trait_ident,
cc::Fastcall,
"fastcall",
all(windows, target_arch = "x86")
);
cc_shorthand_in!(
new_stdcall_in,
$trait_ident,
cc::Stdcall,
"stdcall",
all(windows, target_arch = "x86")
);
cc_shorthand_in!(
new_cdecl_in,
$trait_ident,
cc::Cdecl,
"cdecl",
all(windows, target_arch = "x86")
);
cc_shorthand_in!(
new_thiscall_in,
$trait_ident,
cc::Thiscall,
"thiscall",
all(windows, target_arch = "x86")
);
cc_shorthand_in!(
new_win64_in,
$trait_ident,
cc::Win64,
"win64",
all(windows, target_arch = "x86_64")
);
cc_shorthand_in!(
new_variadic_in,
$trait_ident,
cc::Variadic,
"`C` variadic",
feature = "c_variadic"
);
}
#[cfg(feature = "global_jit_alloc")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[doc = $fn_trait_doc]
#[doc = $ty_name_doc]
pub type $non_sync_alias<'a, B, A = GlobalJitAlloc> = $ty_name<B, dyn Any + 'a, A>;
#[cfg(not(feature = "global_jit_alloc"))]
#[doc = $fn_trait_doc]
#[doc = $ty_name_doc]
pub type $non_sync_alias<'a, B, A> = $ty_name<B, dyn Any + 'a, A>;
#[cfg(feature = "global_jit_alloc")]
#[cfg_attr(docsrs, doc(cfg(all())))]
#[doc = $fn_trait_doc]
#[doc = $non_sync_alias_doc]
#[doc = $ty_name_doc]
pub type $sync_alias<'a, B, A = GlobalJitAlloc> = $ty_name<B, $sync_alias_bound, A>;
#[cfg(not(feature = "global_jit_alloc"))]
#[doc = $fn_trait_doc]
#[doc = $non_sync_alias_doc]
#[doc = $ty_name_doc]
pub type $sync_alias<'a, B, A> = $ty_name<B, $sync_alias_bound, A>;
};
}
bare_closure_impl!(
ty_name: BareFnOnceAny,
erased_ty_name: UntypedBareFnOnce,
non_sync_alias: BareFnOnce,
sync_alias: BareFnOnceSync,
sync_bounds: (Send), sync_alias_bound: dyn Send + 'a,
trait_ident: FnOnceThunk,
thunk_template: THUNK_TEMPLATE_ONCE,
bare_toggle: cfg(any()),
bare_receiver: Self,
fn_trait_doc: "[`FnOnce`]",
ty_name_doc: "[`BareFnOnceAny`]",
with_cc_doc: "[`with_cc`](BareFnOnceAny::with_cc)",
with_cc_in_doc: "[`with_cc_in`](BareFnOnceAny::with_cc_in)",
try_with_cc_in_doc: "[`try_with_cc_in`](BareFnOnceAny::try_with_cc_in)",
non_sync_alias_doc: "[`BareFnOnce`]",
sync_alias_doc: "[`BareFnOnceSync`]",
sync_alias_bound_doc: "[`S = dyn Send + 'a`](Send)",
safety_doc: "- The function has been called before.\n
- The closure is not `Send`, if calling from a different thread than the current one."
);
bare_closure_impl!(
ty_name: BareFnMutAny,
erased_ty_name: UntypedBareFnMut,
non_sync_alias: BareFnMut,
sync_alias: BareFnMutSync,
sync_bounds: (Send), sync_alias_bound: dyn Send + 'a,
trait_ident: FnMutThunk,
thunk_template: THUNK_TEMPLATE_MUT,
bare_toggle: cfg(all()),
bare_receiver: &Self,
fn_trait_doc: "[`FnMut`]",
ty_name_doc: "[`BareFnMutAny`]",
with_cc_doc: "[`with_cc`](BareFnMutAny::with_cc)",
with_cc_in_doc: "[`with_cc_in`](BareFnMutAny::with_cc_in)",
try_with_cc_in_doc: "[`try_with_cc_in`](BareFnMutAny::try_with_cc_in)",
non_sync_alias_doc: "[`BareFnMut`]",
sync_alias_doc: "[`BareFnMutSync`]",
sync_alias_bound_doc: "[`S = dyn Send + 'a`](Send)",
safety_doc: "- The mutable borrow induced by a previous call is still active (e.g. through recursion)
or concurrent (has no happens-before relationship) with the current one.\n
- The closure is not `Send`, if calling from a different thread than the current one."
);
bare_closure_impl!(
ty_name: BareFnAny,
erased_ty_name: UntypedBareFn,
non_sync_alias: BareFn,
sync_alias: BareFnSync,
sync_bounds: (Sync),
sync_alias_bound: dyn Send + Sync + 'a,
trait_ident: FnThunk,
thunk_template: THUNK_TEMPLATE,
bare_toggle: cfg(all()),
bare_receiver: &Self,
fn_trait_doc: "[`Fn`]",
ty_name_doc: "[`BareFnAny`]",
with_cc_doc: "[`with_cc`](BareFnAny::with_cc)",
with_cc_in_doc: "[`with_cc_in`](BareFnAny::with_cc_in)",
try_with_cc_in_doc: "[`try_with_cc_in`](BareFnAny::try_with_cc_in)",
non_sync_alias_doc: "[`BareFn`]",
sync_alias_doc: "[`BareFnSync`]",
sync_alias_bound_doc: "[`S = dyn Send + Sync + 'a`](Sync)",
safety_doc: "- The closure is not `Sync`, if calling from a different thread than the current one."
);