extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::spanned::Spanned;
#[proc_macro_attribute]
pub fn lucet_hostcall(_attr: TokenStream, item: TokenStream) -> TokenStream {
let in_internals = std::env::var("CARGO_PKG_NAME").unwrap() == "lucet-runtime-internals";
let mut hostcall = syn::parse_macro_input!(item as syn::ItemFn);
let hostcall_ident = hostcall.sig.ident.clone();
let attrs = hostcall.attrs.clone();
let vis = hostcall.vis.clone();
hostcall
.attrs
.retain(|attr| !attr.path.is_ident("no_mangle"));
hostcall.vis = syn::Visibility::Inherited;
let mut raw_sig = hostcall.sig.clone();
raw_sig.unsafety = Some(syn::Token![unsafe](raw_sig.span()));
raw_sig.abi = Some(syn::parse_quote!(extern "C"));
let vmctx_mod = if in_internals {
quote! { lucet_runtime_internals::vmctx }
} else {
quote! { lucet_runtime::vmctx }
};
if let Some(arg0) = raw_sig.inputs.iter_mut().nth(0) {
let lucet_vmctx: syn::FnArg = syn::parse_quote!(vmctx_raw: *mut #vmctx_mod::lucet_vmctx);
*arg0 = lucet_vmctx;
}
let impl_args = hostcall
.sig
.inputs
.iter()
.skip(1)
.map(|arg| match arg {
syn::FnArg::Receiver(_) => {
let s = syn::Token![self](arg.span());
quote!(#s)
}
syn::FnArg::Typed(syn::PatType { pat, .. }) => quote!(#pat),
})
.collect::<Vec<_>>();
let termination_details = if in_internals {
quote! { lucet_runtime_internals::instance::TerminationDetails }
} else {
quote! { lucet_runtime::TerminationDetails }
};
let raw_hostcall = quote! {
#(#attrs)*
#vis
#raw_sig {
#[inline(always)]
#hostcall
let mut vmctx = #vmctx_mod::Vmctx::from_raw(vmctx_raw);
#vmctx_mod::VmctxInternal::instance_mut(&mut vmctx).uninterruptable(|| {
let res = std::panic::catch_unwind(move || {
#hostcall_ident(&mut #vmctx_mod::Vmctx::from_raw(vmctx_raw), #(#impl_args),*)
});
match res {
Ok(res) => res,
Err(e) => {
match e.downcast::<#termination_details>() {
Ok(details) => {
#vmctx_mod::Vmctx::from_raw(vmctx_raw).terminate_no_unwind(*details)
},
Err(e) => std::panic::resume_unwind(e),
}
}
}
})
}
};
raw_hostcall.into()
}