smart_ir_macro/
lib.rs

1// Copyright (c) The Ant Group Core Contributors
2// Copyright (c) The Smart Intermediate Representation Contributors
3// SPDX-License-Identifier: Apache-2.0
4
5use quote::ToTokens;
6use syn::{Data, DataStruct, Fields, Type};
7extern crate proc_macro;
8extern crate syn;
9#[macro_use]
10extern crate quote;
11
12#[proc_macro_derive(MetaDataNode)]
13pub fn derive_md_node(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
14    let input = proc_macro2::TokenStream::from(input);
15
16    let output = derive_md_node_impl(input);
17
18    proc_macro::TokenStream::from(output)
19}
20
21fn derive_md_node_impl(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
22    let ast: syn::DeriveInput = syn::parse2(input).unwrap();
23    let name = ast.ident;
24    quote! {
25        impl MetaDataNode for #name {
26            fn get_metadata(&self) -> &indexmap::IndexMap<String, u32> {
27                &self.metadata
28            }
29
30            fn get_metadata_mut(&mut self) -> &mut indexmap::IndexMap<String, u32> {
31                &mut self.metadata
32            }
33        }
34    }
35}
36
37fn map_fields<F>(fields: &Fields, mapper: F) -> proc_macro2::TokenStream
38where
39    F: FnMut((&proc_macro2::Ident, &Type)) -> proc_macro2::TokenStream,
40{
41    proc_macro2::TokenStream::from_iter(
42        fields
43            .iter()
44            .map(|field| (field.ident.as_ref().unwrap(), &field.ty))
45            .map(mapper),
46    )
47}
48
49/// metadata definition derive macro
50/// attribute MetaDataKey must be set
51/// This macro will generate getters and setters for all fields of the struct,
52/// defining the conversion and registration of the struct to metadata
53///
54/// # Example
55///
56/// ```rust
57/// use smart_ir::cfg::ir::{MetaData, IRContext, Literal, IntValue};
58/// use smart_ir_macro::{MetadataDefinition, MetaDataKey};
59///
60/// #[derive(MetadataDefinition, PartialEq, Eq, Default)]
61/// #[MetaDataKey(smart_foo_bar)]
62/// struct FooBar {
63///     foo: i32,
64///     bar: String,
65/// };
66///
67/// fn foo() {
68///     let mut ctx = IRContext::default();
69///     let mut instr = Instr::new(InstrDescription::br(0));
70///     let foo_bar = FooBar::default()
71///         .foo(10)
72///         .bar("hello world".to_string());
73///     FooBar::add_to_context(&mut ctx,&mut instr, &foo_bar);
74///     let metadata = FooBar::get_from_context(&ctx, &instr);
75///     assert!(metadata.is_none());
76///     assert_eq!(foo_bar, metadata.unwrap());
77/// }
78///
79/// ```
80/// expanded rust code will be like:
81/// ```rust
82/// struct FooBar {
83///     foo: i32,
84///     bar: String,
85/// };
86///
87/// impl FooBar {
88///     pub fn foo(mut self, value: i32) -> Self {
89///         self.foo = value;
90///         self
91///     }
92///     pub fn get_foo(mut self) -> i32 {
93///         self.foo
94///     }
95///     pub fn bar(mut self, value: String) -> Self {
96///         self.bar = value;
97///         self
98///     }
99///     pub fn get_bar(mut self) -> String {
100///         self.bar
101///     }
102///     pub fn from(metadata: &MetaData) -> Result<Self, String> {
103///         Ok(Self {
104///             foo: metadata.get_operand(0).get_i32()?,
105///             bar: metadata.get_operand(1).get_string()?,
106///         })
107///     }
108///     pub fn to_metadata(&self) -> MetaData {
109///     let mut metadata = MetaData::default();
110///         metadata.push_field(Literal::Int(IntLiteral::I32(self.foo)));
111///         metadata.push_field(Literal::Str(self.bar.clone()));
112///         metadata
113///     }
114///     pub fn get_metadata_key() -> String {
115///        "smart_foo_bar".to_string()
116///     }
117///     pub fn add_to_context(
118///         ctx: &mut IRContext,
119///         md_node: &mut dyn MetaDataNode,
120///         loc: &DebugLocation,
121///        ) {
122///         let md_idx = ctx.add_metadata(loc.to_metadata());
123///         let metadata = md_node.get_metadata_mut();
124///         metadata.insert("smart_foo_bar".to_string(), md_idx);
125///     }
126///     pub fn get_from_context(ctx: &IRContext, md_node: &dyn MetaDataNode) -> Option<DebugLocation> {
127///         let metadata = md_node.get_metadata();
128///         let md_idx = metadata.get("smart_foo_bar")?;
129///         DebugLocation::from(ctx.get_metadata(md_idx)?).ok()
130///     }
131/// }
132/// ```
133///
134#[proc_macro_derive(MetadataDefinition, attributes(MetaDataKey))]
135pub fn derive_md_definition(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
136    let input = proc_macro2::TokenStream::from(input);
137
138    let output = derive_md_definition_impl(input);
139    proc_macro::TokenStream::from(output)
140}
141
142fn derive_md_definition_impl(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
143    let ast: syn::DeriveInput = syn::parse2(input).unwrap();
144
145    let struct_name = ast.ident;
146    let mut meta_data_key = None;
147    for attr in &ast.attrs {
148        if attr.path().is_ident("MetaDataKey") {
149            //#[MetaDataKey(...)]
150            attr.parse_nested_meta(|meta| match meta.path.get_ident() {
151                Some(id) => {
152                    meta_data_key = Some(id.clone());
153                    Ok(())
154                }
155                None => Err(syn::Error::new(
156                    proc_macro2::Span::call_site(),
157                    "expand MetadataDefinition failed: MetaDataKey isn't set",
158                )),
159            })
160            .unwrap();
161        }
162    }
163    match ast.data {
164        Data::Struct(DataStruct { fields, .. }) => {
165            let fields_access = map_fields(&fields, |(ident, ty)| {
166                let getter_name = format_ident!("get_{}", ident);
167                quote!(pub fn #ident(mut self, value: #ty) -> Self {
168                        self.#ident = value;
169                        self
170                    }
171                    pub fn #getter_name(&self) -> #ty {
172                        self.#ident.clone()
173                    }
174                )
175            });
176
177            let fields_to_literals = map_fields(&fields, |(ident, ty)| field_to_literal(ident, ty));
178            let mut fields_count = 0;
179            let literals_to_fields = map_fields(&fields, |(ident, ty)| {
180                let result = literal_to_field(fields_count, ident, ty);
181                fields_count += 1;
182                result
183            });
184            let meta_data_key = meta_data_key
185                .unwrap_or_else(|| {
186                    panic!("expand MetadataDefinition failed: MetaDataKey isn't set")
187                })
188                .to_string();
189            let meta_data_key =
190                syn::LitStr::new(meta_data_key.as_str(), proc_macro2::Span::call_site());
191
192            quote!(
193                impl #struct_name {
194                    // getter and setter
195                    #fields_access
196
197                    pub fn from(metadata: &MetaData) -> Result<Self, String> {
198                        Ok(Self {
199                            #literals_to_fields
200                        })
201                    }
202
203                    pub fn to_metadata(&self) -> MetaData {
204                        let mut metadata = MetaData::default();
205                        #fields_to_literals
206                        metadata
207                    }
208
209                    pub fn get_metadata_key() -> String {
210                        #meta_data_key.to_string()
211                    }
212
213                    pub fn add_to_context(
214                        ctx: &IRContext,
215                        md_node: &mut dyn MetaDataNode,
216                        loc: &#struct_name,
217                    ) {
218                        let md_idx = ctx.add_metadata(loc.to_metadata());
219                        let metadata = md_node.get_metadata_mut();
220                        metadata.insert(#meta_data_key.to_string(), md_idx);
221                    }
222                    pub fn get_from_context(ctx: &IRContext, md_node: &dyn MetaDataNode) -> Option<#struct_name> {
223                        let metadata = md_node.get_metadata();
224                        let md_idx = metadata.get(#meta_data_key)?;
225                        #struct_name::from(ctx.get_metadata(md_idx).as_ref()?).ok()
226                    }
227                }
228            )
229        }
230        _ => panic!(),
231    }
232}
233
234fn field_to_literal(ident: &proc_macro2::Ident, ty: &Type) -> proc_macro2::TokenStream {
235    match ty {
236        Type::Path(val) => {
237            let ty_str = val.path.get_ident().unwrap().to_string();
238            return match ty_str.as_str() {
239                "String" => quote!(
240                    metadata.push_field(Literal::Str(self.#ident.clone()));
241                ),
242                "bool" => quote!(
243                    metadata.push_field(Literal::Bool(self.#ident));
244                ),
245                "i8" => quote!(
246                    metadata.push_field(Literal::Int(IntLiteral::I8(self.#ident)));
247                ),
248                "i16" => quote!(
249                    metadata.push_field(Literal::Int(IntLiteral::I16(self.#ident)));
250                ),
251                "i32" => quote!(
252                    metadata.push_field(Literal::Int(IntLiteral::I32(self.#ident)));
253                ),
254                "i64" => quote!(
255                    metadata.push_field(Literal::Int(IntLiteral::I64(self.#ident)));
256                ),
257                "i128" => quote!(
258                    metadata.push_field(Literal::Int(IntLiteral::I128(self.#ident)));
259                ),
260                "u8" => quote!(
261                    metadata.push_field(Literal:Int(IntLiteral:::U8(self.#ident)));
262                ),
263                "u16" => quote!(
264                    metadata.push_field(Literal::Int(IntLiteral::U16(self.#ident)));
265                ),
266                "u32" => quote!(
267                    metadata.push_field(Literal::Int(IntLiteral::U32(self.#ident)));
268                ),
269                "u64" => quote!(
270                    metadata.push_field(Literal::Int(IntLiteral::U64(self.#ident)));
271                ),
272                "u128" => quote!(
273                    metadata.push_field(Literal::Int(IntLiteral::U128(self.#ident)));
274                ),
275                _ => {
276                    let token_stream = ty.to_token_stream();
277                    panic!(
278                        "{}",
279                        format!(
280                        "MetadataDefinition expand failed: unsupported field ty: {token_stream}"
281                    )
282                    )
283                }
284            };
285        }
286        _ => {
287            let token_stream = ty.to_token_stream();
288            panic!(
289                "{}",
290                format!("MetadataDefinition expand failed: unsupported field ty: {token_stream}",)
291            )
292        }
293    }
294}
295
296fn literal_to_field(
297    field_idx: i32,
298    ident: &proc_macro2::Ident,
299    ty: &Type,
300) -> proc_macro2::TokenStream {
301    let field_idx_lit = syn::LitInt::new(
302        field_idx.to_string().as_str(),
303        proc_macro2::Span::call_site(),
304    );
305    match ty {
306        Type::Path(val) => {
307            let ty_str = val.path.get_ident().unwrap().to_string();
308            let result = match ty_str.as_str() {
309                "String" => quote!(
310                    #ident: metadata.get_operand(#field_idx_lit).get_string()?,
311                ),
312                "bool" => quote!(
313                    #ident: metadata.get_operand(#field_idx_lit).get_bool()?,
314                ),
315                "i8" => quote!(
316                    #ident: metadata.get_operand(#field_idx_lit).get_i8()?,
317                ),
318                "i16" => quote!(
319                    #ident: metadata.get_operand(#field_idx_lit).get_i16()?,
320                ),
321                "i32" => quote!(
322                    #ident: metadata.get_operand(#field_idx_lit).get_i32()?,
323                ),
324                "i64" => quote!(
325                    #ident: metadata.get_operand(#field_idx_lit).get_i64()?,
326                ),
327                "i128" => quote!(
328                    #ident: metadata.get_operand(#field_idx_lit).get_i128()?,
329                ),
330                "u8" => quote!(
331                    #ident: metadata.get_operand(#field_idx_lit).get_u8()?,
332                ),
333                "u16" => quote!(
334                    #ident: metadata.get_operand(#field_idx_lit).get_u16()?,
335                ),
336                "u32" => quote!(
337                    #ident: metadata.get_operand(#field_idx_lit).get_u32()?,
338                ),
339                "u64" => quote!(
340                    #ident: metadata.get_operand(#field_idx_lit).get_u64()?,
341                ),
342                "u128" => quote!(
343                    #ident: metadata.get_operand(#field_idx_lit).get_u128()?,
344                ),
345                _ => {
346                    let token_stream = ty.to_token_stream();
347                    panic!(
348                        "{}",
349                        format!(
350                        "MetadataDefinition expand failed: unsupported field ty: {token_stream}",
351                    )
352                    )
353                }
354            };
355            result
356        }
357        _ => {
358            let token_stream = ty.to_token_stream();
359            panic!(
360                "{}",
361                format!("MetadataDefinition expand failed: unsupported field ty:  {token_stream}")
362            )
363        }
364    }
365}