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         match end {
107             Some(v) => {
108                 // Argument needs an into translation if it doesn't match the start
109                 if start != v {
110                     args.extend(quote! { #end::from(#ident), });
111                 } else {
112                     args.extend(quote! {#ident,});
113                 }
114             }
115             // If there's no end type, just consume it instead.
116             None => (),
117         }
118     }
119 
120     let mut stmts = proc_macro2::TokenStream::new();
121     for stmt in parsed_cptr.stmts {
122         stmts.extend(quote! { #stmt });
123     }
124 
125     let dispatcher_str = quote!(#dispatcher).to_string();
126     let tokens = quote! {
127         #[no_mangle]
128         extern "C" fn #ident(#params) {
129             #stmts
130                 (get_dispatchers()
131                     .lock()
132                     .expect("Couldn't lock dispatchers!")
133                     .get::<#dispatcher>()
134                     .expect(concat!("Couldn't find dispatcher type: ", #dispatcher_str))
135                     .clone()
136                     .lock()
137                     .expect(concat!("Couldn't lock specific dispatcher: ", #dispatcher_str))
138                     .dispatch)(#rpath(#args));
139             }
140     };
141 
142     TokenStream::from(tokens)
143 }
144 
145 // TODO: Replace below macro with a public crate, such as https://crates.io/crates/adorn
146 #[proc_macro_attribute]
147 /// Macro to check if the profile has been initialized
148 ///
149 /// Function who applies this macro should also include log::warn and the self must implement
150 /// fn is_initialized(&self) -> bool
151 ///
152 /// Example:
153 ///     ```
154 ///     use log::warn;
155 ///     #[profile_enabled_or]
156 ///     fn foo(&self) {
157 ///         // actual code
158 ///     }
159 ///     ```
160 ///     expands as
161 ///     ```
162 ///     use log::warn;
163 ///     fn foo(&self) {
164 ///         if !self.is_enabled() {
165 ///             warn!("Tried to {} but internal hasn't been enabled", "foo");
166 ///             return ;
167 ///         }
168 ///         // actual code
169 ///     }
170 ///     ```
171 /// One can specify a return value on uninitialized case
172 ///     ```
173 ///     use log::warn;
174 ///     #[profile_enabled_or("not ready")]
175 ///     fn foo(&self) -> &str {
176 ///         // actual code
177 ///     }
178 ///     ```
179 ///     expands as
180 ///     ```
181 ///     use log::warn;
182 ///     fn foo(&self) -> &str {
183 ///         if !self.is_enabled() {
184 ///             warn!("Tried to {} but internal hasn't been enabled", "foo");
185 ///             return "not ready";
186 ///         }
187 ///         // actual code
188 ///         return "success"
189 ///     }
190 ///     ```
profile_enabled_or(attr: TokenStream, item: TokenStream) -> TokenStream191 pub fn profile_enabled_or(attr: TokenStream, item: TokenStream) -> TokenStream {
192     generate_profile_enabled_or_tokenstream(item, attr.to_string())
193 }
194 
195 /// Similar to profile_enabled_or but return Default::default() when profile is not enabled.
196 #[proc_macro_attribute]
profile_enabled_or_default(_attr: TokenStream, item: TokenStream) -> TokenStream197 pub fn profile_enabled_or_default(_attr: TokenStream, item: TokenStream) -> TokenStream {
198     generate_profile_enabled_or_tokenstream(item, String::from("Default::default()"))
199 }
200 
generate_profile_enabled_or_tokenstream(item: TokenStream, attr_string: String) -> TokenStream201 fn generate_profile_enabled_or_tokenstream(item: TokenStream, attr_string: String) -> TokenStream {
202     let mut input = syn::parse_macro_input!(item as syn::ItemFn);
203 
204     let fn_name = input.sig.ident.to_string();
205 
206     let ret_stmt: proc_macro2::TokenStream = format!("return {};", attr_string).parse().unwrap();
207 
208     let check_block = quote::quote! {
209         if !self.is_enabled() {
210             warn!("Tried to {} but internal hasn't been enabled", #fn_name);
211             #ret_stmt
212         }
213     };
214 
215     input.block.stmts.insert(0, syn::parse(check_block.into()).unwrap());
216 
217     let output = quote::quote! {
218         #input
219     };
220 
221     output.into()
222 }
223 
224 /// Generate impl cxx::ExternType for the trivial types in bindings.
225 ///
226 /// This is only needed if they need to be share with the cxx-bridge blocks.
227 ///
228 /// Usage (assume the C++ type some::ns::sample_t is defined in types/some_samples.h):
229 /// ```ignore
230 /// #[gen_cxx_extern_trivial]
231 /// type SampleType = bindings::some::ns::sample_t;
232 /// ```
233 ///
234 /// Which generates the type info below for cxx-bridge:
235 /// ```ignore
236 /// unsafe impl cxx::ExternType for SampleType {
237 ///     type Id = cxx::type_id!("some::ns::sample_t");
238 ///     type Kind = cxx::kind::Trivial;
239 /// }
240 /// ```
241 ///
242 /// To use the binding type in a cxx::bridge block, include the header and (optionally) assign
243 /// the namespace and name for the C++ type.
244 /// ```ignore
245 /// #[cxx::bridge]
246 /// mod ffi {
247 ///     unsafe extern "C++" {
248 ///         include!("types/some_samples.h");
249 ///
250 ///         #[namespace = "some::ns"]
251 ///         #[cxx_name = "sample_t"]
252 ///         type SampleType = super::SampleType;
253 ///     }
254 /// }
255 /// ```
256 #[proc_macro_attribute]
gen_cxx_extern_trivial(_attr: TokenStream, item: TokenStream) -> TokenStream257 pub fn gen_cxx_extern_trivial(_attr: TokenStream, item: TokenStream) -> TokenStream {
258     let input = syn::parse_macro_input!(item as syn::ItemType);
259 
260     let ident = input.ident.clone();
261 
262     let segs = match *input.ty {
263         Type::Path(syn::TypePath {
264             qself: None,
265             path: Path { leading_colon: None, ref segments },
266         }) => segments,
267         _ => panic!("Unsupported type"),
268     };
269 
270     let mut iter = segs.into_iter();
271 
272     match iter.next() {
273         Some(seg) if seg.ident == "bindings" => {}
274         _ => panic!("Unexpected type: Must starts with \"bindings::\""),
275     }
276 
277     match iter.clone().next() {
278         Some(seg) if seg.ident == "root" => {
279             // Skip the "root" module in bindings
280             iter.next();
281         }
282         _ => {}
283     }
284 
285     let cxx_ident = iter.map(|seg| seg.ident.to_string()).collect::<Vec<String>>().join("::");
286 
287     if cxx_ident.is_empty() {
288         panic!("Empty cxx ident");
289     }
290 
291     quote! {
292         #input
293 
294         unsafe impl cxx::ExternType for #ident {
295             type Id = cxx::type_id!(#cxx_ident);
296             type Kind = cxx::kind::Trivial;
297         }
298     }
299     .into()
300 }
301