1 //! Macro for topshim
2 
3 extern crate proc_macro;
4 
5 use proc_macro::TokenStream;
6 use quote::{format_ident, quote};
7 use syn::parse::{Parse, ParseStream, Result};
8 use syn::{parse_macro_input, Block, Ident, Path, Stmt, Token, Type};
9 
10 /// Parsed structure for callback variant
11 struct CbVariant {
12     dispatcher: Type,
13     fn_pair: (Ident, Path),
14     arg_pairs: Vec<(Type, Option<Type>)>,
15     stmts: Vec<Stmt>,
16 }
17 
18 impl Parse for CbVariant {
parse(input: ParseStream) -> Result<Self>19     fn parse(input: ParseStream) -> Result<Self> {
20         // First thing should be the dispatcher
21         let dispatcher: Type = input.parse()?;
22         input.parse::<Token![,]>()?;
23 
24         // Name and return type are parsed
25         let name: Ident = input.parse()?;
26         input.parse::<Token![->]>()?;
27         let rpath: Path = input.parse()?;
28 
29         let mut arg_pairs: Vec<(Type, Option<Type>)> = Vec::new();
30         let mut stmts: Vec<Stmt> = Vec::new();
31 
32         while input.peek(Token![,]) {
33             // Discard the comma
34             input.parse::<Token![,]>()?;
35 
36             // Check if we're expecting the final Block
37             if input.peek(syn::token::Brace) {
38                 let block: Block = input.parse()?;
39                 stmts.extend(block.stmts);
40 
41                 break;
42             }
43 
44             // Grab the next type argument
45             let start_type: Type = input.parse()?;
46 
47             if input.peek(Token![->]) {
48                 // Discard ->
49                 input.parse::<Token![->]>()?;
50 
51                 // Try to parse Token![_]. If that works, we will
52                 // consume this value and not pass it forward.
53                 // Otherwise, try to parse as syn::Type and pass forward for
54                 // conversion.
55                 if input.peek(Token![_]) {
56                     input.parse::<Token![_]>()?;
57                     arg_pairs.push((start_type, None));
58                 } else {
59                     let end_type: Type = input.parse()?;
60                     arg_pairs.push((start_type, Some(end_type)));
61                 }
62             } else {
63                 arg_pairs.push((start_type.clone(), Some(start_type)));
64             }
65         }
66 
67         // TODO: Validate there are no more tokens; currently they are ignored.
68         Ok(CbVariant { dispatcher, fn_pair: (name, rpath), arg_pairs, stmts })
69     }
70 }
71 
72 #[proc_macro]
73 /// Implement C function to convert callback into enum variant.
74 ///
75 /// Expected syntax:
76 ///     ```compile_fail
77 ///     cb_variant(DispatcherType, function_name -> EnumType::Variant, args..., {
78 ///         // Statements (maybe converting types)
79 ///         // Args in order will be _0, _1, etc.
80 ///     })
81 ///     ```
82 ///
83 /// args can do conversions inline as well. In order for conversions to work, the relevant
84 /// From<T> trait should also be implemented.
85 ///
86 /// Example:
87 ///     u32 -> BtStatus (requires impl From<u32> for BtStatus)
88 ///
89 /// To consume a value during conversion, you can use "Type -> _". This is useful when you want
90 /// to convert a pointer + size into a single Vec (i.e. using ptr_to_vec).
91 ///
92 /// Example:
93 ///     u32 -> _
cb_variant(input: TokenStream) -> TokenStream94 pub fn cb_variant(input: TokenStream) -> TokenStream {
95     let parsed_cptr = parse_macro_input!(input as CbVariant);
96 
97     let dispatcher = parsed_cptr.dispatcher;
98     let (ident, rpath) = parsed_cptr.fn_pair;
99 
100     let mut params = proc_macro2::TokenStream::new();
101     let mut args = proc_macro2::TokenStream::new();
102     for (i, (start, end)) in parsed_cptr.arg_pairs.iter().enumerate() {
103         let ident = format_ident!("_{}", i);
104         params.extend(quote! { #ident: #start, });
105 
106         if let Some(v) = end {
107             // Argument needs an into translation if it doesn't match the start
108             if start != v {
109                 args.extend(quote! { #end::from(#ident), });
110             } else {
111                 args.extend(quote! {#ident,});
112             }
113         }
114     }
115 
116     let mut stmts = proc_macro2::TokenStream::new();
117     for stmt in parsed_cptr.stmts {
118         stmts.extend(quote! { #stmt });
119     }
120 
121     let dispatcher_str = quote!(#dispatcher).to_string();
122     let tokens = quote! {
123         #[no_mangle]
124         extern "C" fn #ident(#params) {
125             #stmts
126                 (get_dispatchers()
127                     .lock()
128                     .expect("Couldn't lock dispatchers!")
129                     .get::<#dispatcher>()
130                     .expect(concat!("Couldn't find dispatcher type: ", #dispatcher_str))
131                     .clone()
132                     .lock()
133                     .expect(concat!("Couldn't lock specific dispatcher: ", #dispatcher_str))
134                     .dispatch)(#rpath(#args));
135             }
136     };
137 
138     TokenStream::from(tokens)
139 }
140 
141 // TODO: Replace below macro with a public crate, such as https://crates.io/crates/adorn
142 #[proc_macro_attribute]
143 /// Macro to check if the profile has been initialized
144 ///
145 /// Function who applies this macro should also include log::warn and the self must implement
146 /// fn is_initialized(&self) -> bool
147 ///
148 /// Example:
149 ///     ```
150 ///     use log::warn;
151 ///     #[profile_enabled_or]
152 ///     fn foo(&self) {
153 ///         // actual code
154 ///     }
155 ///     ```
156 ///     expands as
157 ///     ```
158 ///     use log::warn;
159 ///     fn foo(&self) {
160 ///         if !self.is_enabled() {
161 ///             warn!("Tried to {} but internal hasn't been enabled", "foo");
162 ///             return ;
163 ///         }
164 ///         // actual code
165 ///     }
166 ///     ```
167 /// One can specify a return value on uninitialized case
168 ///     ```
169 ///     use log::warn;
170 ///     #[profile_enabled_or("not ready")]
171 ///     fn foo(&self) -> &str {
172 ///         // actual code
173 ///     }
174 ///     ```
175 ///     expands as
176 ///     ```
177 ///     use log::warn;
178 ///     fn foo(&self) -> &str {
179 ///         if !self.is_enabled() {
180 ///             warn!("Tried to {} but internal hasn't been enabled", "foo");
181 ///             return "not ready";
182 ///         }
183 ///         // actual code
184 ///         return "success"
185 ///     }
186 ///     ```
profile_enabled_or(attr: TokenStream, item: TokenStream) -> TokenStream187 pub fn profile_enabled_or(attr: TokenStream, item: TokenStream) -> TokenStream {
188     generate_profile_enabled_or_tokenstream(item, attr.to_string())
189 }
190 
191 /// Similar to profile_enabled_or but return Default::default() when profile is not enabled.
192 #[proc_macro_attribute]
profile_enabled_or_default(_attr: TokenStream, item: TokenStream) -> TokenStream193 pub fn profile_enabled_or_default(_attr: TokenStream, item: TokenStream) -> TokenStream {
194     generate_profile_enabled_or_tokenstream(item, String::from("Default::default()"))
195 }
196 
generate_profile_enabled_or_tokenstream(item: TokenStream, attr_string: String) -> TokenStream197 fn generate_profile_enabled_or_tokenstream(item: TokenStream, attr_string: String) -> TokenStream {
198     let mut input = syn::parse_macro_input!(item as syn::ItemFn);
199 
200     let fn_name = input.sig.ident.to_string();
201 
202     let ret_stmt: proc_macro2::TokenStream = format!("return {};", attr_string).parse().unwrap();
203 
204     let check_block = quote::quote! {
205         if !self.is_enabled() {
206             warn!("Tried to {} but internal hasn't been enabled", #fn_name);
207             #ret_stmt
208         }
209     };
210 
211     input.block.stmts.insert(0, syn::parse(check_block.into()).unwrap());
212 
213     let output = quote::quote! {
214         #input
215     };
216 
217     output.into()
218 }
219 
220 /// Generate impl cxx::ExternType for the trivial types in bindings.
221 ///
222 /// This is only needed if they need to be share with the cxx-bridge blocks.
223 ///
224 /// Usage (assume the C++ type some::ns::sample_t is defined in types/some_samples.h):
225 /// ```ignore
226 /// #[gen_cxx_extern_trivial]
227 /// type SampleType = bindings::some::ns::sample_t;
228 /// ```
229 ///
230 /// Which generates the type info below for cxx-bridge:
231 /// ```ignore
232 /// unsafe impl cxx::ExternType for SampleType {
233 ///     type Id = cxx::type_id!("some::ns::sample_t");
234 ///     type Kind = cxx::kind::Trivial;
235 /// }
236 /// ```
237 ///
238 /// To use the binding type in a cxx::bridge block, include the header and (optionally) assign
239 /// the namespace and name for the C++ type.
240 /// ```ignore
241 /// #[cxx::bridge]
242 /// mod ffi {
243 ///     unsafe extern "C++" {
244 ///         include!("types/some_samples.h");
245 ///
246 ///         #[namespace = "some::ns"]
247 ///         #[cxx_name = "sample_t"]
248 ///         type SampleType = super::SampleType;
249 ///     }
250 /// }
251 /// ```
252 #[proc_macro_attribute]
gen_cxx_extern_trivial(_attr: TokenStream, item: TokenStream) -> TokenStream253 pub fn gen_cxx_extern_trivial(_attr: TokenStream, item: TokenStream) -> TokenStream {
254     let input = syn::parse_macro_input!(item as syn::ItemType);
255 
256     let ident = input.ident.clone();
257 
258     let segs = match *input.ty {
259         Type::Path(syn::TypePath {
260             qself: None,
261             path: Path { leading_colon: None, ref segments },
262         }) => segments,
263         _ => panic!("Unsupported type"),
264     };
265 
266     let mut iter = segs.into_iter();
267 
268     match iter.next() {
269         Some(seg) if seg.ident == "bindings" => {}
270         _ => panic!("Unexpected type: Must starts with \"bindings::\""),
271     }
272 
273     match iter.clone().next() {
274         Some(seg) if seg.ident == "root" => {
275             // Skip the "root" module in bindings
276             iter.next();
277         }
278         _ => {}
279     }
280 
281     let cxx_ident = iter.map(|seg| seg.ident.to_string()).collect::<Vec<String>>().join("::");
282 
283     if cxx_ident.is_empty() {
284         panic!("Empty cxx ident");
285     }
286 
287     quote! {
288         #input
289 
290         unsafe impl cxx::ExternType for #ident {
291             type Id = cxx::type_id!(#cxx_ident);
292             type Kind = cxx::kind::Trivial;
293         }
294     }
295     .into()
296 }
297