#![feature(proc_macro_diagnostic)]
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::Span;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
use syn::token::Comma;
use syn::{AttributeArgs, Ident, ItemFn, Lit, Meta, MetaList, NestedMeta};
use quote::quote;
fn parse_two_ints(args: Punctuated<NestedMeta, Comma>) -> (u64, u64) {
if args.len() != 2 {
args.span()
.unstable()
.error("needs two numbers as parameters")
.emit();
}
let a = if let NestedMeta::Literal(Lit::Int(first)) = &args[0] {
first.value()
} else {
args[0]
.span()
.unstable()
.error("first parameter not an int literal")
.emit();
0
};
let b = if let NestedMeta::Literal(Lit::Int(second)) = &args[1] {
second.value()
} else {
args[1]
.span()
.unstable()
.error("second parameter not an int literal")
.emit();
0
};
(a, b)
}
fn should_panic(fun: &ItemFn) -> bool {
fun.attrs
.iter()
.find(|&attr| {
attr.path
.segments
.iter()
.find(|&path_segment| path_segment.ident == "should_panic")
.is_some()
})
.is_some()
}
#[proc_macro_attribute]
pub fn x86test(args: TokenStream, input: TokenStream) -> TokenStream {
let args: Vec<NestedMeta> = syn::parse_macro_input!(args as AttributeArgs);
let input_fn = syn::parse_macro_input!(input as ItemFn);
let mut physical_memory: (u64, u64) = (0, 0);
let mut ioport_enable: (u64, u64) = (0, 0);
let should_panic = should_panic(&input_fn);
let mut should_halt = false;
for arg in args {
if let NestedMeta::Meta(Meta::List(MetaList {
ident,
paren_token: _,
nested,
})) = arg
{
match ident.to_string().as_str() {
"ram" => {
physical_memory = parse_two_ints(nested);
}
"ioport" => {
ioport_enable = parse_two_ints(nested);
}
x => unreachable!("unsupported attribute: {}", x),
}
} else if let NestedMeta::Meta(Meta::Word(ident)) = arg {
match ident.to_string().as_str() {
"should_halt" => should_halt = true,
x => unreachable!("unsupported attribute: {}", x),
}
}
}
let physical_memory_tuple = {
let (a, b) = physical_memory;
quote! { (#a, #b) }
};
let ioport_enable_tuple = {
let (a, b) = ioport_enable;
quote! { (#a as u16, #b as u32) }
};
let struct_name = format!("{}_genkvmtest", input_fn.ident);
let struct_ident = Ident::new(struct_name.as_str(), Span::call_site());
let test_name = format!("{}", input_fn.ident);
let fn_ident = input_fn.ident.clone();
let ast = quote! {
#[allow(non_upper_case_globals, unused_attributes)]
#[test_case]
static #struct_ident: X86TestFn = X86TestFn {
name: #test_name,
ignore: false,
identity_map: true,
physical_memory: #physical_memory_tuple,
ioport_enable: #ioport_enable_tuple,
should_panic: #should_panic,
should_halt: #should_halt,
testfn: x86test::StaticTestFn(|| {
#fn_ident();
unsafe { x86test::outw(0xf4, 0x00); }
})
};
#[allow(unused_attributes)]
#input_fn
};
ast.into()
}