This commit is contained in:
Jean-Marie Mineau 2023-07-21 11:36:33 +02:00
commit ab1d6f4f8c
9 changed files with 1452 additions and 0 deletions

View file

@ -0,0 +1,2 @@
/target
/Cargo.lock

View file

@ -0,0 +1,15 @@
[package]
name = "androscalpel_serializer_derive"
version = "0.1.0"
edition = "2021"
[lib]
proc-macro = true
[dependencies]
proc-macro2 = "1"
quote = "1"
syn = { version = "2", features = ["parsing"] }
[dev-dependencies]
androscalpel_serializer = { path = "../androscalpel_serializer" }

View file

@ -0,0 +1,518 @@
use proc_macro2::TokenStream;
use quote::{format_ident, quote, quote_spanned};
use syn::parse::{Parse, ParseStream};
use syn::spanned::Spanned;
use syn::{
parse_macro_input, Attribute, Data, DeriveInput, Field, Fields, Ident, Index, Meta, MetaList,
Token, Type, Variant,
};
/// Derive the type Serializable.
///
/// For simple case, it is just a concatenation of the Serializable fields.
///
/// ## Until
///
/// For list of data, we can serialize data until we find some specific data. To do so we need
/// the type of the repeated data `D`, the type of the symbole at the end of the list `U`, and
/// the specific value of the symbole at the end of the list `end`.
///
/// For example, we can deserialize a Vec<u8> until we find 0x0000:
///
/// `[0, 1, 2, 3, 0, 0]` would be serialized as `vec![0, 1, 2, 3]`
///
/// In this example, `D` is `u8`, `U` is `u16` and `end` is `0x0000`.
///
/// To define a field using this method, the type of field must implement the trait
/// `SerializableUntil<D, U>`, `D` and `U` must implement `Serializable`, and the field
/// must be marked with the attribute `#[until(D, U, end)]`:
///
/// ```
/// pub use androscalpel_serializer::*;
///
/// #[derive(Serializable, PartialEq, Debug)]
/// struct Example {
/// #[until(u8, u16, 0)]
/// a: Vec<u8>,
/// }
///
/// assert_eq!(
/// Example::deserialize_from_slice(&[0, 1, 2, 3, 0, 0]).unwrap(),
/// Example { a: vec![0, 1, 2, 3] }
/// );
/// ```
///
#[proc_macro_derive(Serializable, attributes(until, prefix, prefix_type))]
pub fn derive_serializable(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = input.ident;
let params = ParamsStruct::parse(&input.attrs);
let implem_serialize = get_implem_serialize(&input.data, &params);
let implem_deserialize = get_implem_deserialize(&input.data, &params);
let implem_size = get_implem_size(&input.data, &params);
let expanded = quote! {
impl androscalpel_serializer::Serializable for #name {
fn serialize(&self, output: &mut dyn std::io::Write) -> androscalpel_serializer::Result<()> {
#implem_serialize
}
fn deserialize(input: &mut dyn androscalpel_serializer::ReadSeek) -> androscalpel_serializer::Result<Self> {
#implem_deserialize
}
fn size(&self) -> usize {
#implem_size
}
}
};
proc_macro::TokenStream::from(expanded)
}
/// Parsed Parameters for the `#[until(D, U, end)]` attribute.
/// `D` is the type of the repeated data, `U` is the type of `end`,
/// `end` is the object that mark the end of the repetition of `U`s.
struct UntilParams(Ident, Ident, TokenStream);
impl Parse for UntilParams {
fn parse(input: ParseStream) -> syn::Result<Self> {
let data_type = input.parse()?;
input.parse::<Token![,]>()?;
let end_type = input.parse()?;
input.parse::<Token![,]>()?;
let until = input.parse()?;
Ok(Self(data_type, end_type, until))
}
}
/// Parsed Parameters for the `#[prefix(val)]` attribute.
struct PrefixParams(TokenStream);
impl Parse for PrefixParams {
fn parse(input: ParseStream) -> syn::Result<Self> {
let value = input.parse()?;
Ok(Self(value))
}
}
/// Parsed Parameters for the `#[prefix_type(T)]` attribute.
struct PrefixTypeParams(TokenStream);
impl Parse for PrefixTypeParams {
fn parse(input: ParseStream) -> syn::Result<Self> {
let data_type = input.parse()?;
Ok(Self(data_type))
}
}
/// All the attributes parameters for a struct/enum
#[derive(Default)]
struct ParamsStruct {
pub prefix_type: Option<PrefixTypeParams>,
}
/// All the attributes parameters for a field
#[derive(Default)]
struct ParamsField {
pub until: Option<UntilParams>,
}
/// All the attributes parameters for a variant
#[derive(Default)]
struct ParamsVariant {
pub prefix: Option<PrefixParams>,
}
impl ParamsStruct {
fn parse(attrs: &[Attribute]) -> Self {
let mut params = ParamsStruct::default();
for attr in attrs {
match &attr.meta {
Meta::List(MetaList { path, .. }) if path.is_ident("until") => {
panic!("Structur/Enum cannot take the attribut 'until'")
}
Meta::List(MetaList { path, .. }) if path.is_ident("prefix") => {
panic!("Structur/Enum cannot take the attribut 'prefix'")
}
Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix_type") => {
params.prefix_type = Some(syn::parse2(tokens.clone()).unwrap());
}
_ => unimplemented!(),
}
}
params
}
}
impl ParamsField {
fn parse(attrs: &[Attribute]) -> Self {
let mut params = ParamsField::default();
for attr in attrs {
match &attr.meta {
Meta::List(MetaList { path, tokens, .. }) if path.is_ident("until") => {
params.until = Some(syn::parse2(tokens.clone()).unwrap())
}
Meta::List(MetaList { path, .. }) if path.is_ident("prefix") => {
panic!("Fields cannot take the attribut 'prefix'")
}
Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => {
panic!("Fields cannot take the attribut 'prefix_type'")
}
_ => unimplemented!(),
}
}
params
}
}
impl ParamsVariant {
fn parse(attrs: &[Attribute]) -> Self {
let mut params = ParamsVariant::default();
for attr in attrs {
match &attr.meta {
Meta::List(MetaList { path, .. }) if path.is_ident("until") => {
panic!("Variant cannot take the attribut 'until'")
}
Meta::List(MetaList { path, tokens, .. }) if path.is_ident("prefix") => {
params.prefix = Some(syn::parse2(tokens.clone()).unwrap());
}
Meta::List(MetaList { path, .. }) if path.is_ident("prefix_type") => {
panic!("Variant cannot take the attribut 'prefix_type'")
}
_ => unimplemented!(),
}
}
params
}
}
/// Return the match statement an enum variant.
fn get_enum_match(variant: &Variant) -> TokenStream {
match variant.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
quote! { #name }
});
quote_spanned! { variant.span() =>
{#(#recurse,)*}
}
}
Fields::Unnamed(ref fields) => {
let names: Vec<_> = fields
.unnamed
.iter()
.enumerate()
.map(|(i, _)| format_ident!("field{}", i))
.collect();
quote_spanned! { variant.span() =>
(#(#names,)*)
}
}
Fields::Unit => quote! {},
}
}
/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::size`]
/// for a specific field `f` accessible using `field_ref`.
fn get_implem_size_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
let params = ParamsField::parse(&f.attrs);
match (&f.ty, params) {
(
_,
ParamsField {
until: Some(UntilParams(d, u, until)),
},
) => quote_spanned! { f.span() =>
androscalpel_serializer::SerializableUntil::<#d, #u>::size(&#field_ref, #until)
},
(Type::Array(_), ParamsField { until: None }) => quote_spanned! { f.span() =>
#field_ref.iter().map(androscalpel_serializer::Serializable::size).sum::<usize>()
},
(_, ParamsField { until: None }) => quote_spanned! { f.span() =>
androscalpel_serializer::Serializable::size(&#field_ref)
},
}
}
/// Return the implementation of the [`androscalpel_serializer::Serializable::size`].
fn get_implem_size(data: &Data, params: &ParamsStruct) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
get_implem_size_for_field(f, quote! { self.#name })
});
quote! {
0 #(+ #recurse)*
}
}
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index::from(i);
get_implem_size_for_field(f, quote! { self.#index })
});
quote! {
0 #(+ #recurse)*
}
}
Fields::Unit => quote!(0),
},
Data::Enum(ref data) => {
let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect(
"Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute",
);
let recurse = data.variants.iter().map(|v| {
let ident = &v.ident;
let params = ParamsVariant::parse(&v.attrs);
let prefix = params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute",
);
let PrefixParams(val) = prefix;
let match_ = get_enum_match(v);
let body = match v.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
get_implem_size_for_field(f, quote! { *#name })
});
quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )*
}
}
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let ident = format_ident!("field{}", i);
get_implem_size_for_field(f, quote! { *#ident })
});
quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) #(+#recurse )*
}
}
Fields::Unit => quote! { <#prefix_ty as androscalpel_serializer::Serializable>::size(&#val) },
};
quote_spanned! { v.span() =>
Self::#ident #match_ => { #body },
}
});
quote! { match self {
#(#recurse)*
}}
}
Data::Union(_) => unimplemented!(),
}
}
/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::serialize`]
/// for a specific field `f` accessible using `field_ref`.
fn get_implem_serialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
let params = ParamsField::parse(&f.attrs);
// TODO: Improve error handling
match (&f.ty, params) {
(
_,
ParamsField {
until: Some(UntilParams(d, u, until)),
},
) => quote_spanned! { f.span() =>
androscalpel_serializer::SerializableUntil::<#d, #u>::serialize(&#field_ref, output, #until)?;
},
(Type::Array(_), ParamsField { until: None }) => quote_spanned! { f.span() =>
for x in #field_ref {
androscalpel_serializer::Serializable::serialize(&x, output)?;
}
},
(_, ParamsField { until: None }) => quote_spanned! { f.span() =>
androscalpel_serializer::Serializable::serialize(&#field_ref, output)?;
},
}
}
/// Return the implementation of the [`androscalpel_serializer::Serializable::serialize`].
fn get_implem_serialize(data: &Data, params: &ParamsStruct) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
get_implem_serialize_for_field(f, quote! { self.#name })
});
quote! {
#(#recurse)*
Ok(())
}
}
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let index = Index::from(i);
get_implem_serialize_for_field(f, quote! { self.#index })
});
quote! {
#(#recurse)*
Ok(())
}
}
Fields::Unit => quote!(0),
},
Data::Enum(ref data) => {
let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect(
"Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute",
);
let recurse = data.variants.iter().map(|v| {
let ident = &v.ident;
let params = ParamsVariant::parse(&v.attrs);
let prefix = params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute",
);
let PrefixParams(val) = prefix;
let match_ = get_enum_match(v);
let body = match v.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
get_implem_serialize_for_field(f, quote! { *#name })
});
quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
#(#recurse)*
}
}
Fields::Unnamed(ref fields) => {
let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| {
let ident = format_ident!("field{}", i);
get_implem_serialize_for_field(f, quote! { *#ident })
});
quote_spanned! { v.span() =>
<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
#(#recurse)*
}
}
Fields::Unit => quote! {
<#prefix_ty as androscalpel_serializer::Serializable>::serialize(&#val, output)?;
},
};
quote_spanned! { v.span() =>
Self::#ident #match_ => { #body },
}
});
quote! {
match self {
#(#recurse)*
}
Ok(())
}
}
Data::Union(_) => unimplemented!(),
}
}
/// Return the implementation of the computation of [`androscalpel_serializer::Serializable::deserialize`]
/// for a specific field `f` accessible using `field_ref`.
fn get_implem_deserialize_for_field(f: &Field, field_ref: TokenStream) -> TokenStream {
let params = ParamsField::parse(&f.attrs);
let ty = &f.ty;
// TODO: Improve error handling
match (ty, params) {
(
_,
ParamsField {
until: Some(UntilParams(d, u, until)),
},
) => quote_spanned! { f.span() =>
#field_ref <#ty as androscalpel_serializer::SerializableUntil::<#d, #u>>::deserialize(input, #until)?,
},
(Type::Array(arr), ParamsField { until: None }) => {
let len = &arr.len;
let arr_ty = &arr.elem;
quote_spanned! { f.span() =>
#field_ref {
let mut vec_ = vec![];
for _ in 0..(#len) {
vec_.push(<#arr_ty as androscalpel_serializer::Serializable>::deserialize(input)?);
}
vec_.try_into().unwrap()
},
}
}
(_, ParamsField { until: None }) => quote_spanned! { f.span() =>
#field_ref <#ty as androscalpel_serializer::Serializable>::deserialize(input)?,
},
}
}
/// Return the implementation of the [`androscalpel_serializer::Serializable::deserialize`].
fn get_implem_deserialize(data: &Data, params: &ParamsStruct) -> TokenStream {
match *data {
Data::Struct(ref data) => match data.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
get_implem_deserialize_for_field(f, quote! { #name: })
});
quote! {
Ok(
Self {
#(#recurse)*
}
)
}
}
Fields::Unnamed(ref fields) => {
let recurse = fields
.unnamed
.iter()
.map(|f| get_implem_deserialize_for_field(f, quote! {}));
quote! {
Ok(Self(#(#recurse)*))
}
}
Fields::Unit => quote!(0),
},
Data::Enum(ref data) => {
let PrefixTypeParams(prefix_ty) = params.prefix_type.as_ref().expect(
"Cannot derive Serializable for an enum without the #[prefix_type(T)] attribute",
);
let recurse = data.variants.iter().map(|v| {
let v_ident = &v.ident;
let v_params = ParamsVariant::parse(&v.attrs);
let PrefixParams(val) = v_params.prefix.expect(
"Cannot derive Serializable for variant without the #[prefix(val)] attribute",
);
match v.fields {
Fields::Named(ref fields) => {
let recurse = fields.named.iter().map(|f| {
let name = &f.ident;
get_implem_deserialize_for_field(f, quote! { #name: })
});
quote_spanned! { v.span() =>
if #val == prefix {
return Ok(Self::#v_ident {#(#recurse)*});
}
}
}
Fields::Unnamed(ref fields) => {
let recurse = fields
.unnamed
.iter()
.map(|f| get_implem_deserialize_for_field(f, quote! {}));
quote_spanned! { v.span() =>
if #val == prefix {
return Ok(Self::#v_ident (#(#recurse)*));
}
}
}
Fields::Unit => quote_spanned! { v.span() =>
if #val == prefix {
return Ok(Self::#v_ident);
}
},
}
});
quote! {
let prefix = <#prefix_ty as androscalpel_serializer::Serializable>::deserialize(input)?;
#(#recurse)*
Err(androscalpel_serializer::Error::SerializeationError(format!(
"Found prefix {:?} that did not match any variant.", prefix
)))
}
}
Data::Union(_) => unimplemented!(),
}
}