blob: be866882fc70ef98b547a8c53443c656fde32110 [file]
/*
* Copyright 2021, Google LLC
*
* SPDX-License-Identifier: Apache-2.0
*/
use proc_macro::TokenStream;
use quote::{ToTokens, quote, format_ident};
use syn::{FnArg, Type, ReturnType, parse_quote};
fn derive_test_wrapper(func: syn::ItemFn) -> TokenStream {
let attributes = &func.attrs;
let signature = &func.sig;
let name = &signature.ident;
let export_name = format_ident!("test_export_{}", name);
let arguments = &signature.inputs;
let return_type = match &signature.output {
ReturnType::Default => ReturnType::Default,
ReturnType::Type(_, ty) => match ty.as_ref() {
Type::Path(ty) => {
let type_ident = ty.path.segments.last().unwrap().ident.to_string();
match &type_ident[..] {
"seL4_Result" => {
parse_quote! { -> isize }
}
_ => signature.output.clone()
}
}
_ => {
signature.output.clone()
}
}
};
let argument_names = arguments.iter().map(|arg| match arg {
FnArg::Typed(ty) => {
let last_token = ty.pat.to_token_stream().into_iter().last().unwrap();
last_token
},
FnArg::Receiver(_) => panic!("unexpected self argument")
});
let function_invocation = quote! { #name(#(#argument_names,)*) };
let body = if return_type == signature.output {
quote! {
#function_invocation
}
} else {
quote! {
match #function_invocation {
Ok(_) => 0,
Err(e) => e as _
}
}
};
TokenStream::from(quote! {
#[export_name = stringify!(#name)]
#(#attributes)*
pub unsafe extern "C" fn #export_name(#arguments) #return_type {
#body
}
})
}
#[proc_macro_attribute]
pub fn export_syscall(_attr: TokenStream, input: TokenStream) -> TokenStream {
let function: syn::ItemFn = syn::parse(input).expect("export_sycall argument should be function declaration");
return derive_test_wrapper(function);
}
#[proc_macro]
pub fn export_syscalls(input: TokenStream) -> TokenStream {
let filename_token: syn::LitStr = syn::parse(input).expect("export_syscalls argument should be string");
let filename = filename_token.value();
let file = std::fs::read_to_string(format!("{}/{}", std::env::var("OUT_DIR").unwrap(), filename)).unwrap();
let mut output = TokenStream::new();
let ast = syn::parse_file(&file).unwrap();
for item in ast.items {
if let syn::Item::Fn(item_fn) = item {
let new_fn = derive_test_wrapper(item_fn);
output.extend(new_fn);
}
}
output
}