use std::{
io::{BufWriter, Write},
path::PathBuf,
process::{Command, Stdio},
};
use quote::{format_ident, quote, ToTokens};
use syn::{parse2, parse_str, File, FnArg, ForeignItem, Item, ReturnType, Signature, Type};
pub fn c_closure_header_include_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}
const SPECIAL_FN_SUFFIX: &str = "_closure_call";
struct ClosureDefinition {
name: String,
signature: Signature,
}
pub fn enhance_closure_bindings(rust_code: &str) -> String {
let mut tree = parse_str::<File>(rust_code).unwrap();
let mut new_enhancements = vec![];
for tree in tree.items.iter_mut() {
new_enhancements.extend(call_recurse(tree, &mut |item| {
let mut enhance = vec![];
match item {
Item::ForeignMod(foreigners) => {
for foreign_item in &mut foreigners.items {
if let ForeignItem::Fn(function) = foreign_item {
let function_name = function.sig.ident.to_string();
if function_name.ends_with(SPECIAL_FN_SUFFIX) {
let closure_name = (&function_name
[0..(function_name.len() - SPECIAL_FN_SUFFIX.len())])
.to_string();
enhance.push(ClosureDefinition {
name: closure_name,
signature: function.sig.clone(),
});
}
}
}
}
_ => {}
}
enhance.iter().flat_map(gen_closure_fns).collect()
}));
}
tree.items.extend(new_enhancements);
let tokenified_source = tree.to_token_stream().to_string();
if let Ok(mut rust_fmt_process) = Command::new("rustfmt")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
{
{
if let Some(mut input) = rust_fmt_process.stdin.as_mut().map(|s| BufWriter::new(s)) {
let _ = input.write_all(tokenified_source.as_bytes());
}
}
rust_fmt_process
.wait_with_output()
.map_err(|_| ())
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout).map_err(|_| ())
} else {
Err(())
}
})
.unwrap_or(tokenified_source)
} else {
tokenified_source
}
}
fn call_recurse<F: FnMut(&mut Item) -> Vec<Item>>(item: &mut Item, f: &mut F) -> Vec<Item> {
let mut enhancements = vec![];
enhancements.extend(f(item));
match item {
Item::Mod(mmod) => {
if let Some(t) = mmod.content.as_mut() {
let new_enhancements =
t.1.iter_mut()
.flat_map(|item| call_recurse(item, f))
.collect::<Vec<_>>();
t.1.extend(new_enhancements);
}
}
_ => {}
};
enhancements
}
fn gen_closure_fns(
&ClosureDefinition {
ref name,
ref signature,
}: &ClosureDefinition,
) -> Vec<Item> {
let closure_name = format_ident!("{}Closure", name);
let release_name = format_ident!("{}_closure_release", name);
let args = signature
.inputs
.iter()
.skip(1)
.map(|arg| match arg {
FnArg::Typed(pat_type) => (*pat_type.ty).clone(),
_ => unreachable!("Functions passed into here should never have a self reference."),
})
.map(|a| a.to_token_stream())
.collect::<Vec<_>>();
let arg_idents = (0..args.len())
.map(|i| format_ident!("p{}", i))
.collect::<Vec<_>>();
let arg_ident_pairs = args
.iter()
.zip(arg_idents.iter())
.map(|(arg, ident)| quote!(#ident: #arg))
.collect::<Vec<_>>();
let mut has_return_value = true;
let return_type = match &signature.output {
ReturnType::Default => {
has_return_value = false;
Type::Verbatim(quote!(()))
}
ReturnType::Type(_, ref ty) => (**ty).clone(),
}
.to_token_stream();
let delete_ret = if has_return_value {
quote!(delete_ret: Some(Self::drop_me::<#return_type>),)
} else {
quote!()
};
let delete_none = if has_return_value {
quote!(delete_ret: None)
} else {
quote!()
};
#[cfg(feature = "no_std")]
let std_or_core = quote!(core);
#[cfg(not(feature = "no_std"))]
let std_or_core = quote!(std);
#[cfg(feature = "no_std")]
let std_or_alloc = quote!(alloc);
#[cfg(not(feature = "no_std"))]
let std_or_alloc = quote!(std);
#[cfg(feature = "no_std")]
let abort_or_zeroed = quote!(::core::mem::zeroed());
#[cfg(not(feature = "no_std"))]
let abort_or_zeroed = quote! {
eprintln!("Function marked as single-use was called more than once, the closure will not be called as that would segfault. Aborting.");
::std::process::abort()
};
let noop = if has_return_value {
quote!()
} else {
quote! {
pub fn new_noop() -> Self {
Self {
data: ::#std_or_core::ptr::null_mut(),
function: None,
delete_data: None,
#delete_none
}
}
}
};
vec![
parse2(
quote! {
impl #closure_name {
unsafe extern "C" fn f_wrapper<F>(f: *mut ::#std_or_core::ffi::c_void, #(#arg_ident_pairs),*) -> #return_type
where
F: FnMut(#(#args),*) -> #return_type,
{
let f = &mut *(f as *mut F);
f(#(#arg_idents),*)
}
unsafe extern "C" fn drop_my_box<T>(t: *mut ::#std_or_core::ffi::c_void) {
#std_or_alloc::boxed::Box::<T>::from_raw(t as *mut T);
}
unsafe extern "C" fn drop_me<T>(_t: T) {
}
pub fn fn_mut<Function>(f: Function) -> Self
where
Function: FnMut(#(#args),*) -> #return_type,
{
Self {
data: #std_or_alloc::boxed::Box::into_raw(#std_or_alloc::boxed::Box::new(f)) as *mut ::#std_or_core::ffi::c_void,
function: Some(Self::f_wrapper::<Function>),
delete_data: Some(Self::drop_my_box::<Function>),
#delete_ret
}
}
pub fn fn_not_mut<Function>(f: Function) -> Self
where
Function: Fn(#(#args),*) -> #return_type,
{
Self {
data: #std_or_alloc::boxed::Box::into_raw(#std_or_alloc::boxed::Box::new(f)) as *mut ::#std_or_core::ffi::c_void,
function: Some(Self::f_wrapper::<Function>),
delete_data: Some(Self::drop_my_box::<Function>),
#delete_ret
}
}
pub fn fn_once<Function>(f: Function) -> Self
where
Function: FnOnce(#(#args),*) -> #return_type,
{
let mut f = Some(f);
Self::fn_mut(move |#(#arg_idents),*| match f.take() {
Some(f) => f(#(#arg_idents),*),
None => {
#abort_or_zeroed
}
})
}
#noop
}
}
).unwrap(),
parse2(
quote! {
impl Drop for #closure_name {
fn drop(&mut self) {
unsafe {
#release_name(self)
}
}
}
}
).unwrap()
]
}