1extern crate proc_macro;
4extern crate rand;
5
6use core::str::FromStr;
7use proc_macro::TokenStream;
8use proc_macro2::Span;
9use quote::quote;
10use rand::{Rng, SeedableRng};
11use std::sync::atomic::{AtomicUsize, Ordering};
12use std::time::{SystemTime, UNIX_EPOCH};
13use syn::{
14 parse::{self, Parse, ParseStream, Result},
15 parse_macro_input,
16 spanned::Spanned,
17 Attribute, Error, Ident, ItemFn, LitInt, ReturnType, Token, Type, TypePath, Visibility,
18};
19
20static CALL_COUNT: AtomicUsize = AtomicUsize::new(0);
21
22enum PhantomEntry {
25 Enum {
26 attributes: Vec<Attribute>,
27 name: String,
28 start: u64,
29 end: u64,
30 enum_type: Ident,
31 variant_list: Vec<String>,
32 },
33 Integer {
34 attributes: Vec<Attribute>,
35 name: String,
36 start: u64,
37 end: u64,
38 },
39 Bool {
40 attributes: Vec<Attribute>,
41 name: String,
42 bit: u64,
43 },
44}
45
46struct PhantomFields {
47 self_member_type: Type,
48 entries: Vec<PhantomEntry>,
49}
50
51impl Parse for PhantomFields {
52 fn parse(input: ParseStream) -> Result<Self> {
53 let _ = input.parse::<Token![self]>()?;
54 let _ = input.parse::<Token![.]>()?;
55 let lit = input.parse::<LitInt>()?;
56 if lit.value() != 0 {
57 return Err(Error::new(lit.span(), "Currently only self.0 is supported"));
58 }
59 let _ = input.parse::<Token![:]>()?;
60 let self_member_type: Type = {
61 let tp = input.parse::<TypePath>()?;
62 let tp_end_string = match tp.path.segments.last().expect("no type given") {
63 syn::punctuated::Pair::Punctuated(path_segment, _colon2) => path_segment.ident.to_string(),
64 syn::punctuated::Pair::End(path_segment) => path_segment.ident.to_string(),
65 };
66 match tp_end_string.as_ref() {
67 "u8" | "i8" | "u16" | "i16" | "u32" | "i32" | "usize" | "isize" | "u64" | "i64" => Type::Path(tp),
68 _ => {
69 return Err(Error::new(tp.span(), format!("Unsupported target type: {:?}", tp_end_string)));
70 }
71 }
72 };
73 let _ = input.parse::<Token![,]>()?;
74 let mut entries: Vec<PhantomEntry> = vec![];
76 'entry_loop: loop {
77 if input.is_empty() {
78 break;
79 }
80 let attributes = input.call(Attribute::parse_outer)?;
81 let name = input.parse::<Ident>()?.to_string();
82 let _ = input.parse::<Token![:]>()?;
83 let start = input.parse::<LitInt>()?.value();
84 let lookahead = input.lookahead1();
85 if lookahead.peek(Token![,]) {
86 entries.push(PhantomEntry::Bool {
88 attributes,
89 name,
90 bit: start,
91 });
92 let _ = input.parse::<Token![,]>()?;
93 continue 'entry_loop;
94 } else if lookahead.peek(Token![-]) {
95 let _ = input.parse::<Token![-]>()?;
97 let end = input.parse::<LitInt>()?.value();
98 let lookahead = input.lookahead1();
99 if lookahead.peek(Token![=]) {
100 let _ = input.parse::<Token![=]>()?;
102 let enum_type = input.parse::<Ident>()?;
103 let mut variant_list = vec![];
104 let _ = input.parse::<Token![<]>()?;
105 'variant_gather_loop: loop {
106 variant_list.push(input.parse::<Ident>()?.to_string());
107 let lookahead = input.lookahead1();
108 if lookahead.peek(Token![>]) {
109 let _ = input.parse::<Token![>]>()?;
111 break 'variant_gather_loop;
112 } else if lookahead.peek(Token![,]) {
113 let _ = input.parse::<Token![,]>()?;
115 continue 'variant_gather_loop;
116 } else {
117 return Err(lookahead.error());
118 }
119 }
120 entries.push(PhantomEntry::Enum {
121 attributes,
122 name,
123 start,
124 end,
125 enum_type,
126 variant_list,
127 });
128 let _ = input.parse::<Token![,]>()?;
129 continue 'entry_loop;
130 } else if lookahead.peek(Token![,]) {
131 entries.push(PhantomEntry::Integer {
133 attributes,
134 name,
135 start,
136 end,
137 });
138 let _ = input.parse::<Token![,]>()?;
139 continue 'entry_loop;
140 } else {
141 return Err(lookahead.error());
142 }
143 } else {
144 return Err(lookahead.error());
145 }
146 }
147 Ok(PhantomFields { self_member_type, entries })
148 }
149}
150
151#[proc_macro]
152pub fn phantom_fields(input: TokenStream) -> TokenStream {
153 let PhantomFields { self_member_type, entries } = parse_macro_input!(input as PhantomFields);
154
155 let mut out_text = String::new();
156
157 for entry in entries.into_iter() {
158 match entry {
159 PhantomEntry::Enum {
160 attributes,
161 name,
162 start,
163 end,
164 enum_type,
165 variant_list,
166 } => {
167 for attribute in attributes.into_iter() {
168 out_text.push_str(&format!("{}\n", TokenStream::from(quote! { #attribute })));
169 }
170 let mask_name = Ident::new(&format!("{}_MASK", name.to_uppercase()), Span::call_site());
171 let read_name = Ident::new(&name.clone(), Span::call_site());
172 let with_name = Ident::new(&format!("with_{}", name), Span::call_site());
173 let width = (end - start) + 1;
174 out_text.push_str(&format!(
175 "{}\n",
176 TokenStream::from(quote! {
177 #[allow(clippy::identity_op)]
178 pub const #mask_name: #self_member_type = ((1<<(#width))-1) << #start;
179
180 #[allow(missing_docs)]
181 pub fn #read_name(self) -> #enum_type
182 })
183 ));
184 out_text.push('{');
185 out_text.push_str(&format!(
186 "{}\n",
187 TokenStream::from(quote! {
188 match (self.0 & Self::#mask_name) >> #start
189 })
190 ));
191 out_text.push('{');
192 let enum_type_string = enum_type.to_string();
193 for (i, variant) in variant_list.iter().enumerate() {
194 out_text.push_str(&format!("{} => {}::{},\n", i, enum_type_string, variant));
195 }
196 if variant_list.len() == (1 << (width - 1)) {
197 out_text.push_str("_ => core::hint::unreachable_unchecked(),");
198 } else {
199 out_text.push_str("_ => unreachable!(),");
200 }
201 out_text.push_str("} }\n");
202 out_text.push_str(&format!(
203 "{}\n",
204 TokenStream::from(quote! {
205 #[allow(missing_docs)]
206 pub const fn #with_name(self, #read_name: #enum_type) -> Self {
207 Self((self.0 & !Self::#mask_name) | (((#read_name as #self_member_type) << #start) & Self::#mask_name))
208 }
209 })
210 ));
211 }
212 PhantomEntry::Integer {
213 attributes,
214 name,
215 start,
216 end,
217 } => {
218 for attribute in attributes.into_iter() {
219 out_text.push_str(&format!("{}\n", TokenStream::from(quote! { #attribute })));
220 }
221 let mask_name = Ident::new(&format!("{}_MASK", name.to_uppercase()), Span::call_site());
222 let read_name = Ident::new(&name.clone(), Span::call_site());
223 let with_name = Ident::new(&format!("with_{}", name), Span::call_site());
224 let width = (end - start) + 1;
225 out_text.push_str(&format!(
226 "{}\n",
227 TokenStream::from(quote! {
228 #[allow(clippy::identity_op)]
229 pub const #mask_name: #self_member_type = ((1<<(#width))-1) << #start;
230
231 #[allow(missing_docs)]
232 pub const fn #read_name(self) -> #self_member_type {
233 (self.0 & Self::#mask_name) >> #start
234 }
235
236 #[allow(missing_docs)]
237 pub const fn #with_name(self, #read_name: #self_member_type) -> Self {
238 Self((self.0 & !Self::#mask_name) | ((#read_name << #start) & Self::#mask_name))
239 }
240 })
241 ));
242 }
243 PhantomEntry::Bool { attributes, name, bit } => {
244 for attribute in attributes.into_iter() {
245 out_text.push_str(&format!("{}\n", TokenStream::from(quote! { #attribute })));
246 }
247 let const_name = Ident::new(&format!("{}_BIT", name.to_uppercase()), Span::call_site());
248 let read_name = Ident::new(&name.clone(), Span::call_site());
249 let with_name = Ident::new(&format!("with_{}", name), Span::call_site());
250 out_text.push_str(&format!(
251 "{}\n",
252 TokenStream::from(quote! {
253 #[allow(clippy::identity_op)]
254 pub const #const_name: #self_member_type = 1 << #bit;
255
256 #[allow(missing_docs)]
257 pub const fn #read_name(self) -> bool {
258 (self.0 & Self::#const_name) != 0
259 }
260
261 #[allow(missing_docs)]
263 pub const fn #with_name(self, bit: bool) -> Self {
264 Self(self.0 ^ (((#self_member_type::wrapping_sub(0, bit as #self_member_type) ^ self.0) & Self::#const_name)))
265 }
266 })
267 ));
268 }
269 };
270 }
271
272 TokenStream::from_str(&out_text).map_err(|e| panic!("{:?}", e)).unwrap()
273}
274
275#[proc_macro_attribute]
299pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
300 let f = parse_macro_input!(input as ItemFn);
301
302 let valid_signature = f.constness.is_none()
304 && f.vis == Visibility::Inherited
305 && f.abi.is_none()
306 && f.decl.inputs.is_empty()
307 && f.decl.generics.params.is_empty()
308 && f.decl.generics.where_clause.is_none()
309 && f.decl.variadic.is_none()
310 && match f.decl.output {
311 ReturnType::Default => false,
312 ReturnType::Type(_, ref ty) => match **ty {
313 Type::Never(_) => true,
314 _ => false,
315 },
316 };
317
318 if !valid_signature {
319 return parse::Error::new(f.span(), "`#[entry]` function must have signature `[unsafe] fn() -> !`")
320 .to_compile_error()
321 .into();
322 }
323
324 if !args.is_empty() {
325 return parse::Error::new(Span::call_site(), "This attribute accepts no arguments")
326 .to_compile_error()
327 .into();
328 }
329
330 let attrs = f.attrs;
332 let block = f.block;
333 let hash = random_ident();
334 let unsafety = f.unsafety;
335
336 quote!(
337 #[export_name = "main"]
338 #(#attrs)*
339 pub #unsafety fn #hash() -> ! #block
340 )
341 .into()
342}
343
344fn random_ident() -> Ident {
346 let secs = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs();
347
348 let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64;
349 let mut seed: [u8; 16] = [0; 16];
350
351 for (i, v) in seed.iter_mut().take(8).enumerate() {
352 *v = ((secs >> (i * 8)) & 0xFF) as u8
353 }
354
355 for (i, v) in seed.iter_mut().skip(8).enumerate() {
356 *v = ((count >> (i * 8)) & 0xFF) as u8
357 }
358
359 let mut rng = rand::rngs::SmallRng::from_seed(seed);
360 Ident::new(
361 &(0..16)
362 .map(|i| {
363 if i == 0 || rng.gen() {
364 ('a' as u8 + rng.gen::<u8>() % 25) as char
365 } else {
366 ('0' as u8 + rng.gen::<u8>() % 10) as char
367 }
368 })
369 .collect::<String>(),
370 Span::call_site(),
371 )
372}