blob: 2daf865aba9d3559f463120f9852b92128206505 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License..
use crate::layout::{self, AlignReq};
use proc_macro2::{Ident, Punct, Span};
use quote::quote;
use std::alloc::Layout;
use syn::parse::{Parse, ParseStream, Result};
use syn::{
parse_quote, punctuated::Punctuated, DeriveInput, Error, Expr, Fields, Lit, LitInt, Meta,
Token, Type,
};
#[allow(dead_code)]
struct KeyValue {
pub ident: Ident,
pub punct: Punct,
pub value: Expr,
}
impl Parse for KeyValue {
fn parse(input: ParseStream) -> Result<Self> {
let ident = input.parse::<Ident>()?;
let punct = input.parse::<Punct>()?;
let value = input.parse::<Expr>()?;
Ok(KeyValue {
ident,
punct,
value,
})
}
}
pub struct AlignArgs {
vars: Vec<KeyValue>,
}
impl Parse for AlignArgs {
fn parse(input: ParseStream) -> Result<Self> {
let keyvalue = Punctuated::<KeyValue, Token![,]>::parse_terminated(input)?;
Ok(AlignArgs {
vars: keyvalue.into_iter().collect(),
})
}
}
impl AlignArgs {
pub fn get_layout(&self) -> Result<Layout> {
let align_iter = self.vars.iter().find(|v| v.ident == "align");
let align: usize = if let Some(align_value) = align_iter {
self.parse_align(&align_value.value)?
} else {
return Err(Error::new(
Span::call_site(),
"Invalid type of align attribute parsing",
));
};
let size_iter = self.vars.iter().find(|v| v.ident == "size");
let size: usize = if let Some(size_value) = size_iter {
self.parse_size(&size_value.value)?
} else {
return Err(Error::new(
Span::call_site(),
"Invalid type of size attribute parsing",
));
};
Layout::from_size_align(size, align)
.map_err(|_e| Error::new(Span::call_site(), "Layout illegal"))
}
fn parse_align(&self, align_expr: &Expr) -> Result<usize> {
if let Expr::Lit(expr) = align_expr {
if let Lit::Int(ref lit) = expr.lit {
let align = lit.base10_parse::<u32>()?;
Ok(align as usize)
} else {
Err(Error::new(
Span::call_site(),
"Invalid align attribute parsing",
))
}
} else {
Err(Error::new(
Span::call_site(),
"Invalid align attribute parsing",
))
}
}
fn parse_size(&self, size_expr: &Expr) -> Result<usize> {
if let Expr::Lit(expr) = size_expr {
if let Lit::Int(ref lit) = expr.lit {
let size = lit.base10_parse::<u32>()?;
Ok(size as usize)
} else {
Err(Error::new(
Span::call_site(),
"Invalid size attribute parsing",
))
}
} else {
Err(Error::new(
Span::call_site(),
"Invalid size attribute parsing",
))
}
}
}
pub struct AlignStruct {
input: DeriveInput,
layout: Layout,
align_layout: Layout,
}
struct FiledExt<'a> {
ty: &'a Type,
name: Option<&'a Ident>,
}
impl<'a> FiledExt<'a> {
pub fn new(ty: &'a Type, name: Option<&'a Ident>) -> Self {
FiledExt { ty, name }
}
pub fn as_origin_named_field(&self) -> proc_macro2::TokenStream {
let ty = &self.ty;
let name = self.name.as_ref().unwrap();
quote! {
#name: #ty,
}
}
}
impl AlignStruct {
pub fn new(layout: Layout, input: DeriveInput) -> Self {
AlignStruct {
input,
layout,
align_layout: unsafe { Layout::from_size_align_unchecked(0, 0) },
}
}
pub fn build(&mut self) -> proc_macro2::TokenStream {
if !(self.is_contains_specified_attr("C")
&& !self.is_contains_specified_attr("align")
&& !self.is_contains_specified_attr("packed"))
{
panic!("Structure attribute must require repr C ");
}
let pad_item = self.get_align_pad_field();
let attrs = self.get_attrs();
let vis = &self.input.vis;
let name = &self.input.ident;
let generics = &self.input.generics;
let fields = &self.get_origin_fields();
let align_attr = &self.generate_align_attr();
quote! {
#attrs
#align_attr
#vis struct #name #generics {
#pad_item,
#fields
}
}
}
#[allow(clippy::filter_next, clippy::collapsible_match)]
fn is_contains_specified_attr(&self, atrr: &str) -> bool {
let mut erxcept_attr = false;
for attr in &self.input.attrs {
let ident = attr.path.get_ident();
let meta = attr.parse_meta();
if ident.map_or(false, |v| *v == "repr") && meta.is_ok() {
if let Ok(Meta::List(ref m)) = meta {
if !m.nested.is_empty() {
erxcept_attr = m
.nested
.iter()
.filter(|x| {
let mut find = false;
if let syn::NestedMeta::Meta(ref s) = x {
if let syn::Meta::Path(p) = s {
find = p.get_ident().map_or(false, |v| *v == atrr);
}
}
find
})
.next()
.is_some();
}
}
}
}
erxcept_attr
}
fn get_attrs(&self) -> proc_macro2::TokenStream {
let attrs = &self.input.attrs;
quote! {
#(#attrs)*
}
}
fn generate_align_attr(&self) -> proc_macro2::TokenStream {
let align = self.align_layout.align();
let litint = LitInt::new(&format!("{}", align), Span::call_site());
quote! {
#[repr(align(#litint))]
}
}
fn get_origin_fields(&self) -> proc_macro2::TokenStream {
if let syn::Data::Struct(ref data) = self.input.data {
if let Fields::Named(ref fields) = data.fields {
let fields: Vec<_> = fields
.named
.iter()
.map(|f| FiledExt::new(&f.ty, f.ident.as_ref()))
.collect();
let item = fields.iter().map(|x| x.as_origin_named_field());
quote! {
#(#item)*
}
} else {
panic!("Structure fields must have names");
}
} else {
panic!("Only supports struct type");
}
}
fn get_align_pad_field(&mut self) -> proc_macro2::TokenStream {
let align_req: &[AlignReq] = &[AlignReq {
offset: 0,
len: self.layout.size(),
}];
let align_layout =
layout::pad_align_to(self.layout, align_req).expect("Align layout illegal");
self.align_layout = align_layout;
let pad = align_layout.size() - align_layout.align() - self.layout.size();
let ty: syn::Type = parse_quote!([u8; #pad]);
let name = Ident::new("no_secret_allowed_in_here", Span::call_site());
quote! {
#name: #ty
}
}
}