1 // Copyright 2024, Linaro Limited 2 // Author(s): Manos Pitsidianakis <manos.pitsidianakis@linaro.org> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 use proc_macro::TokenStream; 6 use quote::quote; 7 use syn::{ 8 parse_macro_input, parse_quote, punctuated::Punctuated, token::Comma, Data, DeriveInput, Field, 9 Fields, Ident, Type, Visibility, 10 }; 11 12 mod utils; 13 use utils::MacroError; 14 15 fn get_fields<'a>( 16 input: &'a DeriveInput, 17 msg: &str, 18 ) -> Result<&'a Punctuated<Field, Comma>, MacroError> { 19 if let Data::Struct(s) = &input.data { 20 if let Fields::Named(fs) = &s.fields { 21 Ok(&fs.named) 22 } else { 23 Err(MacroError::Message( 24 format!("Named fields required for {}", msg), 25 input.ident.span(), 26 )) 27 } 28 } else { 29 Err(MacroError::Message( 30 format!("Struct required for {}", msg), 31 input.ident.span(), 32 )) 33 } 34 } 35 36 fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { 37 let expected = parse_quote! { #[repr(C)] }; 38 39 if input.attrs.iter().any(|attr| attr == &expected) { 40 Ok(()) 41 } else { 42 Err(MacroError::Message( 43 format!("#[repr(C)] required for {}", msg), 44 input.ident.span(), 45 )) 46 } 47 } 48 49 fn derive_object_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { 50 is_c_repr(&input, "#[derive(Object)]")?; 51 52 let name = &input.ident; 53 let parent = &get_fields(&input, "#[derive(Object)]")?[0].ident; 54 55 Ok(quote! { 56 ::qemu_api::assert_field_type!(#name, #parent, 57 ::qemu_api::qom::ParentField<<#name as ::qemu_api::qom::ObjectImpl>::ParentType>); 58 59 ::qemu_api::module_init! { 60 MODULE_INIT_QOM => unsafe { 61 ::qemu_api::bindings::type_register_static(&<#name as ::qemu_api::qom::ObjectImpl>::TYPE_INFO); 62 } 63 } 64 }) 65 } 66 67 #[proc_macro_derive(Object)] 68 pub fn derive_object(input: TokenStream) -> TokenStream { 69 let input = parse_macro_input!(input as DeriveInput); 70 let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); 71 72 TokenStream::from(expanded) 73 } 74 75 #[rustfmt::skip::macros(quote)] 76 fn derive_offsets_or_error(input: DeriveInput) -> Result<proc_macro2::TokenStream, MacroError> { 77 is_c_repr(&input, "#[derive(offsets)]")?; 78 79 let name = &input.ident; 80 let fields = get_fields(&input, "#[derive(offsets)]")?; 81 let field_names: Vec<&Ident> = fields.iter().map(|f| f.ident.as_ref().unwrap()).collect(); 82 let field_types: Vec<&Type> = fields.iter().map(|f| &f.ty).collect(); 83 let field_vis: Vec<&Visibility> = fields.iter().map(|f| &f.vis).collect(); 84 85 Ok(quote! { 86 ::qemu_api::with_offsets! { 87 struct #name { 88 #(#field_vis #field_names: #field_types,)* 89 } 90 } 91 }) 92 } 93 94 #[proc_macro_derive(offsets)] 95 pub fn derive_offsets(input: TokenStream) -> TokenStream { 96 let input = parse_macro_input!(input as DeriveInput); 97 let expanded = derive_offsets_or_error(input).unwrap_or_else(Into::into); 98 99 TokenStream::from(expanded) 100 } 101