#![recursion_limit = "256"]
extern crate proc_macro;
extern crate proc_macro2;
extern crate syn;
#[macro_use]
extern crate log;
use quote::quote;
use proc_macro2::TokenStream;
use syn::{parse_macro_input, DeriveInput};
mod dummy;
mod internals;
mod mutations;
mod serialize;
use quote::quote_spanned;
use syn::spanned::Spanned;
use syn::{Data, Fields};
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
let compile_errors = errors.iter().map(syn::Error::to_compile_error);
quote!(#(#compile_errors)*)
}
#[proc_macro_derive(NewFuzzed, attributes(lain))]
pub fn new_fuzzed(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
mutations::expand_new_fuzzed(&input)
.unwrap_or_else(to_compile_errors)
.into()
}
#[proc_macro_derive(BinarySerialize, attributes(lain))]
pub fn binary_serialize(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
serialize::expand_binary_serialize(&input)
.unwrap_or_else(to_compile_errors)
.into()
}
#[proc_macro_derive(Mutatable, attributes(lain))]
pub fn mutatable_helper(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
mutations::expand_mutatable(&input)
.unwrap_or_else(to_compile_errors)
.into()
}
#[proc_macro_derive(VariableSizeObject)]
pub fn variable_size_object(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let expanded = variable_size_object_helper(&input);
proc_macro::TokenStream::from(expanded)
}
fn variable_size_object_helper(input: &DeriveInput) -> TokenStream {
let imp: TokenStream;
match input.data {
Data::Enum(ref data) => {
let mut simple_variants = true;
for variant in &data.variants {
match variant.fields {
Fields::Unit => {
continue;
}
_ => {
simple_variants = false;
}
}
}
imp = if simple_variants {
quote! {false}
} else {
quote! {true}
};
}
Data::Struct(ref data) => {
if let Fields::Named(ref fields) = data.fields {
if fields.named.len() == 0 {
imp = quote! {false};
} else {
let mut tokens = quote! {false};
for field in fields.named.iter() {
let ty = &field.ty;
tokens.extend(quote_spanned! { field.span() =>
|| <#ty>::is_variable_size()
});
}
imp = tokens;
}
} else {
panic!("Need to add support for unnamed struct fields");
}
}
_ => panic!("Non-enum/struct data types are not supported"),
}
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
quote! {
#[allow(clippy)]
#[allow(unknown_lints)]
impl #impl_generics ::lain::traits::VariableSizeObject for #name #ty_generics #where_clause {
fn is_variable_size() -> bool {
#imp
}
}
}
}
#[proc_macro_derive(FuzzerObject, attributes(lain))]
pub fn fuzzer_object(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let mut base_token_stream = TokenStream::new();
let input = parse_macro_input!(input as DeriveInput);
base_token_stream.extend::<TokenStream>(
mutations::expand_new_fuzzed(&input)
.unwrap_or_else(to_compile_errors)
.into(),
);
base_token_stream.extend::<TokenStream>(
mutations::expand_mutatable(&input)
.unwrap_or_else(to_compile_errors)
.into(),
);
base_token_stream.extend::<TokenStream>(variable_size_object_helper(&input));
base_token_stream.into()
}
#[proc_macro_derive(ToPrimitiveU8)]
pub fn to_primitive_u8(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
to_primitive_of_type(input, quote! {u8})
}
#[proc_macro_derive(ToPrimitiveU16)]
pub fn to_primitive_u16(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
to_primitive_of_type(input, quote! {u16})
}
#[proc_macro_derive(ToPrimitiveU32)]
pub fn to_primitive_u32(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
to_primitive_of_type(input, quote! {u32})
}
#[proc_macro_derive(ToPrimitiveU64)]
pub fn to_primitive_u64(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
to_primitive_of_type(input, quote! {u64})
}
fn to_primitive_of_type(
input: proc_macro::TokenStream,
ty: proc_macro2::TokenStream,
) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let expanded = quote! {
#[allow(clippy)]
#[allow(unknown_lints)]
impl #impl_generics ::lain::traits::ToPrimitive for #name #ty_generics #where_clause {
type Output = #ty;
fn to_primitive(&self) -> #ty {
*self as #ty
}
}
};
debug!("{}", expanded);
proc_macro::TokenStream::from(expanded)
}