#![allow(non_snake_case)]
use std::{ffi::c_void, mem::size_of, process::abort, ptr::null_mut};
use backtrace::Backtrace;
use log::error;
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
impl Closure {
pub fn fn_mut<Arg, Return, Function>(f: Function) -> Self
where
Arg: FromClosureArgPointer,
Function: FnMut(Arg) -> Return + Send + 'static,
{
Self {
data: Box::into_raw(Box::new(f)) as *mut c_void,
function: Some(f_wrapper::<Arg, Return, Function>),
delete_data: Some(delete_me::<Function>),
delete_ret: Some(delete_me::<Return>),
}
}
pub fn fn_not_mut<Arg, Return, Function>(f: Function) -> Self
where
Arg: FromClosureArgPointer,
Function: Fn(Arg) -> Return + Send + 'static,
{
Self {
data: Box::into_raw(Box::new(f)) as *mut c_void,
function: Some(f_wrapper::<Arg, Return, Function>),
delete_data: Some(delete_me::<Function>),
delete_ret: Some(delete_me::<Return>),
}
}
pub fn fn_once<Arg, Return, Function>(f: Function) -> Self
where
Arg: FromClosureArgPointer,
Function: FnOnce(Arg) -> Return + Send + 'static,
{
let mut f = Some(f);
Self::fn_mut(move |arg| match f.take() {
Some(f) => f(arg),
None => {
error!("Function marked as single-use was called more than once, the closure will not be called as that would segfault. Aborting.");
abort()
}
})
}
pub fn new_noop() -> Self {
Self {
data: null_mut(),
function: None,
delete_data: None,
delete_ret: None,
}
}
pub fn rebind_closure_ref<C: ClosureMarkerTrait>(&self) -> &C {
if size_of::<C>() != size_of::<Self>() {
panic!("rebind_closure_ref external definition is not the same size as internal definition. \
`ClosureMarkerTrait` is probably implemented incorrectly. This also might be a bug in c-closures.")
} else {
unsafe { &*(self as *const Self as *const C) }
}
}
pub fn rebind_closure_mut<C: ClosureMarkerTrait>(&mut self) -> &mut C {
if size_of::<C>() != size_of::<Self>() {
panic!("rebind_closure_mut external definition is not the same size as internal definition. \
`ClosureMarkerTrait` is probably implemented incorrectly. This also might be a bug in c-closures.")
} else {
unsafe { &mut *(self as *mut Self as *mut C) }
}
}
}
impl Drop for Closure {
fn drop(&mut self) {
unsafe {
closure_release(self);
}
}
}
unsafe extern "C" fn f_wrapper<Arg, Return, Function>(f: *mut c_void, a: *mut c_void) -> *mut c_void
where
Arg: FromClosureArgPointer,
Function: FnMut(Arg) -> Return + Send + 'static,
{
let f = &mut *(f as *mut Function);
let arg = if a.is_null() && size_of::<Arg>() > 0 {
error!(
"Unexpected null argument received in Closure, the closure will not be called as that would segfault.\n{:?}",
Backtrace::new()
);
None
} else {
Some(Arg::from_arg_ptr(a))
};
arg.map(|arg| Box::into_raw(Box::new(f(arg))) as *mut c_void)
.unwrap_or(null_mut())
}
unsafe extern "C" fn delete_me<T>(t: *mut c_void) {
Box::from_raw(t as *mut T);
}
pub trait ClosureMarkerTrait {}
impl ClosureMarkerTrait for Closure {}
pub trait FromClosureArgPointer {
unsafe fn from_arg_ptr(ptr: *const c_void) -> Self;
}
impl<T: Copy> FromClosureArgPointer for T {
unsafe fn from_arg_ptr(ptr: *const c_void) -> Self {
*(ptr as *const T)
}
}
#[macro_export]
macro_rules! rebind_closure {
($external_name:ty, $closure:expr) => {
{ fn is_closure_type<C: $crate::ClosureMarkerTrait>() {}
if ::std::mem::size_of::<$external_name>() != ::std::mem::size_of::<$crate::Closure>() {
panic!("rebind_ref! macro external definition is not the same size as internal definition. \
`ClosureMarkerTrait` is probably implemented incorrectly.")
} else {
is_closure_type::<$external_name>(); unsafe {
::std::mem::transmute::<$crate::Closure, $external_name>($closure)
}
}
}
};
}
#[cfg(test)]
mod tests {
use std::{
ffi::{CStr, CString},
sync::Arc,
};
use super::*;
#[test]
fn fn_not_mut() {
let y = 4;
let mut closure = Closure::fn_not_mut(move |x: i32| x + x + y);
unsafe {
let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 8);
closure_release_return_value(&mut closure, ret);
closure_release(&mut closure);
}
}
#[test]
fn fn_mut() {
let mut y = 4;
let mut closure = Closure::fn_mut(move |x: i32| {
y *= 2;
x + x + y
});
unsafe {
let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 12);
closure_release_return_value(&mut closure, ret);
let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 20);
closure_release_return_value(&mut closure, ret);
closure_release(&mut closure);
}
}
#[test]
fn fn_once() {
let mut y = 4;
let mut closure = Closure::fn_once(move |x: i32| {
y *= 2;
x + x + y
});
unsafe {
let ret = closure_call(&mut closure, &mut 2 as *mut i32 as _);
assert_eq!(<i32 as FromClosureArgPointer>::from_arg_ptr(ret), 12);
closure_release_return_value(&mut closure, ret);
closure_release(&mut closure);
}
}
#[test]
fn fn_cstring() {
let mut closure = Closure::fn_not_mut(|name: &CStr| {
CString::new(format!("Hello {}", name.to_str().unwrap())).unwrap()
});
let my_name = CString::new("Jacob").unwrap();
unsafe {
let ret = closure_call(&mut closure, &mut my_name.as_c_str() as *mut &CStr as _);
assert_eq!(
(&mut *(ret as *mut CString)).clone().into_string().unwrap(),
"Hello Jacob"
);
closure_release_return_value(&mut closure, ret);
closure_release(&mut closure);
}
}
#[test]
fn fn_drop_test() {
let value = Arc::new(());
let value_clone = value.clone();
let mut closure = Closure::fn_not_mut(move |_: ()| value_clone.clone());
unsafe {
let ret = closure_call(&mut closure, &mut () as *mut () as _);
assert_eq!(Arc::strong_count(&value), 3);
closure_release_return_value(&mut closure, ret);
assert_eq!(Arc::strong_count(&value), 2);
closure_release(&mut closure);
assert_eq!(Arc::strong_count(&value), 1);
}
}
struct NotAClosure;
impl ClosureMarkerTrait for NotAClosure {}
#[test]
#[should_panic]
fn bad_ref_usage() {
let c = Closure::fn_not_mut(|_: ()| ());
c.rebind_closure_ref::<NotAClosure>();
}
#[test]
#[should_panic]
fn bad_mut_usage() {
let mut c = Closure::fn_not_mut(|_: ()| ());
c.rebind_closure_mut::<NotAClosure>();
}
#[test]
fn two_in_scope() {
rebind_closure!(Closure, Closure::new_noop());
rebind_closure!(Closure, Closure::new_noop());
}
}