#![deny(warnings)] extern crate proc_macro; extern crate rand; #[macro_use] extern crate quote; extern crate core; extern crate proc_macro2; #[macro_use] extern crate syn; use proc_macro2::Span; use rand::Rng; use rand::SeedableRng; use std::sync::atomic::{AtomicUsize, Ordering}; use std::time::{SystemTime, UNIX_EPOCH}; use syn::{ parse, spanned::Spanned, Ident, ItemFn, ReturnType, Type, Visibility, }; static CALL_COUNT: AtomicUsize = AtomicUsize::new(0); use proc_macro::TokenStream; /// Attribute to declare the entry point of the program /// /// **IMPORTANT**: This attribute must appear exactly *once* in the dependency graph. Also, if you /// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no /// private modules between the item and the root of the crate); if the item is in the root of the /// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer releases. /// /// The specified function will be called by the reset handler *after* RAM has been initialized. /// If present, the FPU will also be enabled before the function is called. /// /// The type of the specified function must be `[unsafe] fn() -> !` (never ending function) /// /// # Properties /// /// The entry point will be called by the reset handler. /// /// # Examples /// /// - Simple entry point /// /// ``` no_run /// #![no_main] /// use xous_riscv_rt::entry; /// #[entry] /// fn main() -> ! { /// loop { /// /* .. */ /// } /// } /// ``` #[proc_macro_attribute] pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature let valid_signature = f.constness.is_none() && f.vis == Visibility::Inherited && f.abi.is_none() && f.decl.inputs.is_empty() && f.decl.generics.params.is_empty() && f.decl.generics.where_clause.is_none() && f.decl.variadic.is_none() && match f.decl.output { ReturnType::Default => false, ReturnType::Type(_, ref ty) => match **ty { Type::Never(_) => true, _ => false, }, }; if !valid_signature { return parse::Error::new( f.span(), "`#[entry]` function must have signature `[unsafe] fn() -> !`", ) .to_compile_error() .into(); } if !args.is_empty() { return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") .to_compile_error() .into(); } // XXX should we blacklist other attributes? let attrs = f.attrs; let unsafety = f.unsafety; let hash = random_ident(); let stmts = f.block.stmts; quote!( #[export_name = "xous_main"] #(#attrs)* pub #unsafety fn #hash() -> ! { #(#stmts)* } ) .into() } /// Attribute to mark which function will be called at the beginning of the reset handler. /// /// **IMPORTANT**: This attribute can appear at most *once* in the dependency graph. Also, if you /// are using Rust 1.30 the attribute must be used on a reachable item (i.e. there must be no /// private modules between the item and the root of the crate); if the item is in the root of the /// crate you'll be fine. This reachability restriction doesn't apply to Rust 1.31 and newer /// releases. /// /// The function must have the signature of `unsafe fn()`. /// /// The function passed will be called before static variables are initialized. Any access of static /// variables will result in undefined behavior. /// /// # Examples /// /// ``` /// # use riscv_rt_macros::pre_init; /// #[pre_init] /// unsafe fn before_main() { /// // do something here /// } /// /// # fn main() {} /// ``` #[proc_macro_attribute] pub fn pre_init(args: TokenStream, input: TokenStream) -> TokenStream { let f = parse_macro_input!(input as ItemFn); // check the function signature let valid_signature = f.constness.is_none() && f.vis == Visibility::Inherited && f.unsafety.is_some() && f.abi.is_none() && f.decl.inputs.is_empty() && f.decl.generics.params.is_empty() && f.decl.generics.where_clause.is_none() && f.decl.variadic.is_none() && match f.decl.output { ReturnType::Default => true, ReturnType::Type(_, ref ty) => match **ty { Type::Tuple(ref tuple) => tuple.elems.is_empty(), _ => false, }, }; if !valid_signature { return parse::Error::new( f.span(), "`#[pre_init]` function must have signature `unsafe fn()`", ) .to_compile_error() .into(); } if !args.is_empty() { return parse::Error::new(Span::call_site(), "This attribute accepts no arguments") .to_compile_error() .into(); } // XXX should we blacklist other attributes? let attrs = f.attrs; let ident = f.ident; let block = f.block; quote!( #[export_name = "__pre_init"] #(#attrs)* pub unsafe fn #ident() #block ) .into() } // Creates a random identifier fn random_ident() -> Ident { let secs = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let count: u64 = CALL_COUNT.fetch_add(1, Ordering::SeqCst) as u64; let mut seed: [u8; 16] = [0; 16]; for (i, v) in seed.iter_mut().take(8).enumerate() { *v = ((secs >> (i * 8)) & 0xFF) as u8 } for (i, v) in seed.iter_mut().skip(8).enumerate() { *v = ((count >> (i * 8)) & 0xFF) as u8 } let mut rng = rand::rngs::SmallRng::from_seed(seed); Ident::new( &(0..16) .map(|i| { if i == 0 || rng.gen() { ('a' as u8 + rng.gen::() % 25) as char } else { ('0' as u8 + rng.gen::() % 10) as char } }) .collect::(), Span::call_site(), ) }