//! Common macros for Sequoia's FFI crates.
#![recursion_limit="512"]
use std::collections::HashMap;
use std::io::Write;
extern crate lazy_static;
use lazy_static::lazy_static;
extern crate syn;
use syn::spanned::Spanned;
extern crate quote;
extern crate proc_macro;
extern crate proc_macro2;
extern crate sha2;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, ToTokens};
/// Transforms exported functions.
///
/// This macro is used to decorate every function exported from
/// Sequoia. It applies the following transformations:
///
/// - [ffi_catch_abort](attr.ffi_catch_abort.html)
#[proc_macro_attribute]
pub fn extern_fn(attr: TokenStream, item: TokenStream) -> TokenStream {
ffi_catch_abort(attr, item)
}
/// Wraps a function's body in a catch_unwind block, aborting on
/// panics.
///
/// Unwinding the stack across the FFI boundary is [undefined
/// behavior]. We therefore need to wrap every FFI function's body
/// with [catch_unwind]. This macro does that in an unobtrusive
/// manner.
///
/// [undefined behavior]: https://doc.rust-lang.org/nomicon/unwinding.html
/// [catch_unwind]: https://doc.rust-lang.org/std/panic/fn.catch_unwind.html
///
/// # Example
///
/// ```rust,ignore
/// #[ffi_catch_abort]
/// #[no_mangle]
/// pub extern "C" fn sahnetorte() {
/// assert_eq!(2 * 3, 4); // See what happens...
/// }
/// ```
#[proc_macro_attribute]
pub fn ffi_catch_abort(_attr: TokenStream, item: TokenStream) -> TokenStream {
// Parse tokens into a function declaration.
let fun = syn::parse_macro_input!(item as syn::ItemFn);
// Extract all information from the parsed function that we need
// to compose the new function.
let attrs = fun.attrs.iter()
.fold(TokenStream2::new(),
|mut acc, attr| {
acc.extend(attr.clone().into_token_stream());
acc
});
let vis = &fun.vis;
let constness = &fun.constness;
let unsafety = &fun.unsafety;
let asyncness = &fun.asyncness;
let abi = &fun.abi;
let ident = &fun.ident;
let decl = &fun.decl;
let fn_token = &decl.fn_token;
let fn_generics = &decl.generics;
let fn_out = &decl.output;
let mut fn_params = TokenStream2::new();
decl.paren_token.surround(&mut fn_params, |ts| decl.inputs.to_tokens(ts));
let block = &fun.block;
// We wrap the functions body into an catch_unwind, asserting that
// all variables captured by the closure are unwind safe. This is
// safe because we terminate the process on panics, therefore no
// inconsistencies can be observed.
let expanded = quote! {
#attrs #vis #constness #unsafety #asyncness #abi
#fn_token #ident #fn_generics #fn_params #fn_out
{
match ::std::panic::catch_unwind(
::std::panic::AssertUnwindSafe(|| #fn_out #block))
{
Ok(v) => v,
Err(p) => {
unsafe {
::libc::abort();
}
},
}
}
};
// To debug problems with the generated code, just eprintln it:
//
// eprintln!("{}", expanded);
expanded.into()
}
/// Derives FFI functions for a wrapper type.
///
/// # Example
///
/// ```rust,ignore
/// /// Holds a fingerprint.
/// #[::ffi_wrapper_type(prefix = "pgp_",
/// derive = "Clone, Debug, Display, PartialEq, Hash")]
/// pub struct Fingerprint(openpgp::Fingerprint);
/// ```
#[proc_macro_attribute]
pub fn ffi_wrapper_type(args: TokenStr