pub mod traits;
use core::ffi::c_void;
use core::fmt::Debug;
use core::marker::PhantomData;
use core::mem::{MaybeUninit, transmute};
use libffi_sys::ffi_cif;
use traits::{Callback, FfiArgs, FfiReturnType};
use crate::FnPtr;
use crate::abi::Abi;
use crate::closure::BaseClosure;
use crate::closure::raw::{ClosureCallback, Context};
use crate::errors::ClosureAllocationError;
use crate::function::raw::Cif;
use crate::types::Type;
pub struct Closure<ARGS, RET, FN>
where
ARGS: FfiArgs,
RET: FfiReturnType,
FN: Callback<ARGS, RET>,
{
closure: BaseClosure<FN>,
_marker: PhantomData<fn(ARGS) -> RET>,
}
impl<ARGS, RET, FN, const N: usize> Closure<ARGS, RET, FN>
where
ARGS: FfiArgs<TypeArray = [Type; N]>,
RET: FfiReturnType,
FN: Callback<ARGS, RET>,
{
pub fn new(rust_closure: FN) -> Self {
Self::try_new(rust_closure).expect("Libffi was unable to allocate the closure.")
}
pub fn with_abi(rust_closure: FN, abi: Abi) -> Self {
Self::try_with_abi(rust_closure, abi).expect("Libffi was unable to allocate the closure.")
}
pub fn try_new(rust_closure: FN) -> Result<Self, ClosureAllocationError> {
Self::try_with_abi(rust_closure, Abi::default())
}
pub fn try_with_abi(rust_closure: FN, abi: Abi) -> Result<Self, ClosureAllocationError> {
let arg_types = ARGS::as_type_array();
let return_type = RET::return_type(traits::internal::Token);
let closure_handler_fn = unsafe {
transmute::<
unsafe extern "C" fn(
*mut ffi_cif,
*mut MaybeUninit<RET>,
*mut *mut c_void,
*const FN,
),
ClosureCallback,
>(FN::callback)
};
let cif = Cif::new(abi, &arg_types, return_type.as_ref());
let context = Context::new(rust_closure);
let closure = unsafe { BaseClosure::try_new(cif, closure_handler_fn, context)? };
Ok(Self {
closure,
_marker: PhantomData,
})
}
pub fn as_fn_ptr(&self) -> FnPtr {
self.closure.as_fn_ptr()
}
}
impl<ARGS, RET, FN> Debug for Closure<ARGS, RET, FN>
where
ARGS: FfiArgs,
RET: FfiReturnType,
FN: Callback<ARGS, RET>,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Closure")
.field("closure", &self.closure)
.finish()
}
}
#[cfg(test)]
mod tests {
use core::cell::UnsafeCell;
use core::hint::black_box;
use super::*;
use crate::function::{Function, Ret, arg, ret};
use crate::test_utils::{
F32_ARG, F64_ARG, I8_ARG, I16_ARG, I32_ARG, I64_ARG, ISIZE_ARG, PTR_ARG, STRUCT_ARG,
TestStruct, U8_ARG, U16_ARG, U32_ARG, U64_ARG, USIZE_ARG,
};
use crate::types::FfiType;
macro_rules! test_identity_closure_for_type_all_abis {
(fn $testname:ident($type:ty, $value:expr)) => {
#[test]
#[cfg_attr(miri, ignore)]
fn $testname() {
for abi in Abi::ABIS {
let closure = Closure::with_abi(|input: $type| input, abi);
let function = Function::with_abi(
closure.as_fn_ptr(),
&[<$type as FfiType>::ffi_type()],
Some(&<$type as FfiType>::ffi_type()),
abi,
);
let mut return_value = MaybeUninit::<$type>::uninit();
unsafe {
function.call([arg(&$value)], ret(&mut return_value));
}
let return_value = unsafe { return_value.assume_init() };
assert_eq!(
return_value, $value,
"Invalid return value from the identity function using the ABI {abi:?}."
);
}
}
};
}
test_identity_closure_for_type_all_abis!(fn test_i8_identity_closure(i8, I8_ARG));
test_identity_closure_for_type_all_abis!(fn test_i16_identity_closure(i16, I16_ARG));
test_identity_closure_for_type_all_abis!(fn test_i32_identity_closure(i32, I32_ARG));
test_identity_closure_for_type_all_abis!(fn test_i64_identity_closure(i64, I64_ARG));
test_identity_closure_for_type_all_abis!(fn test_isize_identity_closure(isize, ISIZE_ARG));
test_identity_closure_for_type_all_abis!(fn test_u8_identity_closure(u8, U8_ARG));
test_identity_closure_for_type_all_abis!(fn test_u16_identity_closure(u16, U16_ARG));
test_identity_closure_for_type_all_abis!(fn test_u32_identity_closure(u32, U32_ARG));
test_identity_closure_for_type_all_abis!(fn test_u64_identity_closure(u64, U64_ARG));
test_identity_closure_for_type_all_abis!(fn test_usize_identity_closure(usize, USIZE_ARG));
test_identity_closure_for_type_all_abis!(fn test_f32_identity_closure(f32, F32_ARG));
test_identity_closure_for_type_all_abis!(fn test_f64_identity_closure(f64, F64_ARG));
test_identity_closure_for_type_all_abis!(fn test_ptr_identity_closure(*const c_void, PTR_ARG.0));
test_identity_closure_for_type_all_abis!(fn test_test_struct_identity_closure(TestStruct, STRUCT_ARG));
#[test]
#[cfg_attr(miri, ignore)]
fn test_all_types_all_abis() {
for abi in Abi::ABIS {
let closure = Closure::with_abi(
|i8_arg: i8,
i16_arg: i16,
i32_arg: i32,
i64_arg: i64,
isize_arg: isize,
u8_arg: u8,
u16_arg: u16,
u32_arg: u32,
u64_arg: u64,
usize_arg: usize,
f32_arg: f32,
f64_arg: f64,
ptr_arg: *const c_void,
struct_arg: TestStruct| {
assert_eq!(i8_arg, I8_ARG, "`i8` was not identical for ABI {abi:?}.");
assert_eq!(i16_arg, I16_ARG, "`i16` was not identical for ABI {abi:?}.");
assert_eq!(i32_arg, I32_ARG, "`i32` was not identical for ABI {abi:?}.");
assert_eq!(i64_arg, I64_ARG, "`i64` was not identical for ABI {abi:?}.");
assert_eq!(
isize_arg, ISIZE_ARG,
"`isize` was not identical for ABI {abi:?}."
);
assert_eq!(u8_arg, U8_ARG, "`u8` was not identical for ABI {abi:?}.");
assert_eq!(u16_arg, U16_ARG, "`u16` was not identical for ABI {abi:?}.");
assert_eq!(u32_arg, U32_ARG, "`u32` was not identical for ABI {abi:?}.");
assert_eq!(u64_arg, U64_ARG, "`u64` was not identical for ABI {abi:?}.");
assert_eq!(
usize_arg, USIZE_ARG,
"`usize` was not identical for ABI {abi:?}."
);
assert_eq!(f32_arg, F32_ARG, "`f32` was not identical for ABI {abi:?}.");
assert_eq!(f64_arg, F64_ARG, "`f64` was not identical for ABI {abi:?}.");
assert_eq!(
ptr_arg, PTR_ARG.0,
"`*const c_void` was not identical for ABI {abi:?}."
);
assert_eq!(
struct_arg, STRUCT_ARG,
"`TestStruct` was not identical for ABI {abi:?}."
);
},
abi,
);
let function = Function::with_abi(
closure.as_fn_ptr(),
&[
Type::I8,
Type::I16,
Type::I32,
Type::I64,
Type::Isize,
Type::U8,
Type::U16,
Type::U32,
Type::U64,
Type::Usize,
Type::F32,
Type::F64,
Type::Pointer,
TestStruct::ffi_type(),
],
None,
abi,
);
unsafe {
function.call(
[
arg(&I8_ARG),
arg(&I16_ARG),
arg(&I32_ARG),
arg(&I64_ARG),
arg(&ISIZE_ARG),
arg(&U8_ARG),
arg(&U16_ARG),
arg(&U32_ARG),
arg(&U64_ARG),
arg(&USIZE_ARG),
arg(&F32_ARG),
arg(&F64_ARG),
arg(&PTR_ARG.0),
arg(&STRUCT_ARG),
],
Ret::void(),
);
}
}
}
#[test]
#[cfg_attr(miri, ignore)]
fn test_closure_does_not_modify_args() {
for abi in Abi::ABIS {
let i8_arg = UnsafeCell::new(I8_ARG);
let i16_arg = UnsafeCell::new(I16_ARG);
let i32_arg = UnsafeCell::new(I32_ARG);
let i64_arg = UnsafeCell::new(I64_ARG);
let isize_arg = UnsafeCell::new(ISIZE_ARG);
let u8_arg = UnsafeCell::new(U8_ARG);
let u16_arg = UnsafeCell::new(U16_ARG);
let u32_arg = UnsafeCell::new(U32_ARG);
let u64_arg = UnsafeCell::new(U64_ARG);
let usize_arg = UnsafeCell::new(USIZE_ARG);
let f32_arg = UnsafeCell::new(F32_ARG);
let f64_arg = UnsafeCell::new(F64_ARG);
let struct_arg = UnsafeCell::new(STRUCT_ARG);
let ptr_arg = UnsafeCell::new(PTR_ARG);
#[rustfmt::skip]
let closure = Closure::with_abi(|
mut i8_arg: i8, mut i16_arg: i16, mut i32_arg: i32, mut i64_arg: i64,
mut isize_arg: isize, mut u8_arg: u8, mut u16_arg: u16, mut u32_arg: u32,
mut u64_arg: u64, mut usize_arg: usize, mut f32_arg: f32, mut f64_arg: f64,
mut struct_arg: TestStruct, mut ptr_arg: *const c_void,
| {
i8_arg += 1; i16_arg += 1; i32_arg += 1; i64_arg += 1; isize_arg += 1; u8_arg += 1;
u16_arg += 1; u32_arg += 1; u64_arg += 1; usize_arg += 1; f32_arg += 1.; f64_arg += 1.0;
struct_arg.0 += 1; struct_arg.1 += 1; struct_arg.2 += 1; struct_arg.3 += 1;
ptr_arg = unsafe { ptr_arg.byte_add(1) };
black_box((
i8_arg, i16_arg, i32_arg, i64_arg, isize_arg, u8_arg, u16_arg, u32_arg, u64_arg,
usize_arg, f32_arg, f64_arg, struct_arg, ptr_arg
));
}, abi);
#[rustfmt::skip]
let function = Function::with_abi(
closure.as_fn_ptr(),
&[
Type::I8, Type::I16, Type::I32, Type::I64, Type::Isize, Type::U8, Type::U16,
Type::U32, Type::U64, Type::Usize, Type::F32, Type::F64,
<TestStruct as FfiType>::ffi_type(), Type::Pointer,
],
None,
abi,
);
#[rustfmt::skip]
let arg_array = [
arg(&i8_arg), arg(&i16_arg), arg(&i32_arg), arg(&i64_arg), arg(&isize_arg),
arg(&u8_arg), arg(&u16_arg), arg(&u32_arg), arg(&u64_arg), arg(&usize_arg),
arg(&f32_arg), arg(&f64_arg), arg(&struct_arg), arg(&ptr_arg),
];
unsafe {
function.call(arg_array, Ret::void());
}
assert_eq!(i8_arg.into_inner(), I8_ARG);
assert_eq!(i16_arg.into_inner(), I16_ARG);
assert_eq!(i32_arg.into_inner(), I32_ARG);
assert_eq!(i64_arg.into_inner(), I64_ARG);
assert_eq!(isize_arg.into_inner(), ISIZE_ARG);
assert_eq!(u8_arg.into_inner(), U8_ARG);
assert_eq!(u16_arg.into_inner(), U16_ARG);
assert_eq!(u32_arg.into_inner(), U32_ARG);
assert_eq!(u64_arg.into_inner(), U64_ARG);
assert_eq!(usize_arg.into_inner(), USIZE_ARG);
assert_eq!(f32_arg.into_inner(), F32_ARG);
assert_eq!(f64_arg.into_inner(), F64_ARG);
assert_eq!(struct_arg.into_inner(), STRUCT_ARG);
assert_eq!(ptr_arg.into_inner(), PTR_ARG);
}
}
}