#![cfg(feature = "default_jit_alloc")]
#![cfg_attr(feature = "tuple_trait", feature(unboxed_closures))]
#![cfg_attr(feature = "tuple_trait", feature(tuple_trait))]
#![cfg_attr(feature = "tuple_trait", feature(fn_traits))]
use core::marker::PhantomData;
use closure_ffi::{
traits::{FnPtr, FnThunk, ToBoxedDyn},
BareFnAny,
};
pub struct HookCtx<'a, B: FnPtr> {
pub original: B,
phantom: PhantomData<&'a ()>,
}
#[cfg(feature = "tuple_trait")]
mod unstable {
use core::marker::PhantomData;
use closure_ffi::traits::FnPtr;
use super::HookCtx;
struct FnPtrCall<'x, 'y, 'z, B: FnPtr>(B, PhantomData<(&'x mut (), &'y mut (), &'z mut ())>);
impl<'x, 'y, 'z, B: FnPtr> FnOnce<B::Args<'x, 'y, 'z>> for FnPtrCall<'x, 'y, 'z, B>
where
B: 'x + 'y + 'z,
{
type Output = B::Ret<'x, 'y, 'z>;
extern "rust-call" fn call_once(self, args: B::Args<'x, 'y, 'z>) -> Self::Output {
unsafe { self.0.call(args) }
}
}
impl<'x, 'y, 'z, B: FnPtr> FnMut<B::Args<'x, 'y, 'z>> for FnPtrCall<'x, 'y, 'z, B>
where
B: 'x + 'y + 'z,
{
extern "rust-call" fn call_mut(&mut self, args: B::Args<'x, 'y, 'z>) -> Self::Output {
unsafe { self.0.call(args) }
}
}
impl<'x, 'y, 'z, B: FnPtr> Fn<B::Args<'x, 'y, 'z>> for FnPtrCall<'x, 'y, 'z, B>
where
B: 'x + 'y + 'z,
{
extern "rust-call" fn call(&self, args: B::Args<'x, 'y, 'z>) -> Self::Output {
unsafe { self.0.call(args) }
}
}
impl<'a, B: FnPtr> HookCtx<'a, B> {
pub unsafe fn original<'x, 'y, 'z>(
&self,
) -> impl Fn<B::Args<'x, 'y, 'z>, Output = B::Ret<'x, 'y, 'z>>
where
Self: 'x + 'y + 'z,
{
FnPtrCall(self.original, PhantomData)
}
}
}
impl<'a, B: FnPtr> HookCtx<'a, B> {
pub unsafe fn call_original<'x, 'y, 'z>(&self, args: B::Args<'x, 'y, 'z>) -> B::Ret<'x, 'y, 'z>
where
Self: 'x + 'y + 'z,
{
unsafe { self.original.call(args) }
}
#[cfg(not(feature = "tuple_trait"))]
pub unsafe fn original<'x, 'y, 'z>(&self) -> impl Fn(B::Args<'x, 'y, 'z>) -> B::Ret<'x, 'y, 'z>
where
Self: 'x + 'y + 'z,
{
let bare = self.original;
move |args| unsafe { bare.call(args) }
}
}
pub struct Hook<'a, B: FnPtr> {
bare_wrapper: BareFnAny<B, dyn Send + Sync + 'a>,
}
impl<'a, B: FnPtr> Hook<'a, B> {
fn make_context() -> HookCtx<'a, B> {
HookCtx {
original: unsafe { B::from_ptr(core::ptr::dangling()) },
phantom: PhantomData,
}
}
pub fn new<F>(fun: F) -> Self
where
F: ToBoxedDyn<dyn Send + Sync + 'a>,
(B::CC, F): FnThunk<B>,
{
Self {
bare_wrapper: BareFnAny::new(fun),
}
}
pub fn with_cc<CC, F>(cc: CC, fun: F) -> Self
where
F: ToBoxedDyn<dyn Send + Sync + 'a>,
(CC, F): FnThunk<B>,
{
Self {
bare_wrapper: BareFnAny::with_cc(cc, fun),
}
}
pub fn with_ctx<F>(ctx_binder: impl FnOnce(HookCtx<'a, B>) -> F) -> Self
where
F: ToBoxedDyn<dyn Send + Sync + 'a>,
(B::CC, F): FnThunk<B>,
{
let ctx = Self::make_context();
let closure = ctx_binder(ctx);
Self {
bare_wrapper: BareFnAny::new(closure),
}
}
pub fn with_cc_ctx<CC, F>(cc: CC, ctx_binder: impl FnOnce(HookCtx<'a, B>) -> F) -> Self
where
F: ToBoxedDyn<dyn Send + Sync + 'a>,
(CC, F): FnThunk<B>,
{
let ctx = Self::make_context();
let closure = ctx_binder(ctx);
Self {
bare_wrapper: BareFnAny::with_cc(cc, closure),
}
}
pub fn hook(&self) -> B {
self.bare_wrapper.bare()
}
}
#[test]
fn test_inference() {
use closure_ffi::cc;
let borrowed = Box::new(42usize);
let hook: Hook<unsafe extern "C" fn(usize) -> usize> = Hook::new(|arg| arg + *borrowed);
assert_eq!(unsafe { hook.hook()(4) }, 46);
let hook = Hook::with_cc(cc::C, |arg: usize| *borrowed * arg);
assert_eq!(unsafe { hook.hook()(2) }, 84);
let hook: Hook<unsafe extern "C" fn(usize) -> u32> = Hook::with_ctx(|_ctx| move |arg| arg as _);
assert_eq!(unsafe { hook.hook()(42) }, 42);
let hook = Hook::with_cc_ctx(cc::C, |_ctx| move |s: String| s.len());
assert_eq!(unsafe { hook.hook()("abc".to_string()) }, 3);
let _hook: Hook<unsafe extern "C" fn(usize, u32) -> u32> = Hook::with_ctx(|ctx| {
move |x, y| unsafe {
let result = ctx.call_original((x, y));
result + 42
}
});
let _hook = Hook::with_cc_ctx(cc::C, |ctx| {
move |x: usize, y: u32| -> usize {
#[cfg(not(feature = "tuple_trait"))]
let result = unsafe { ctx.original()((x, y)) };
#[cfg(feature = "tuple_trait")]
let result = unsafe { ctx.original()(x, y) };
result + 42
}
});
}