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