@@ -4,10 +4,14 @@ extern crate proc_macro;
44
55use proc_macro:: TokenStream ;
66
7- use proc_macro2:: Span ;
8- use quote:: { quote, TokenStreamExt } ;
9- use syn:: parse:: { Parse , ParseStream } ;
10- use syn:: { parse_macro_input, DeriveInput , Generics , Ident , ItemFn , ItemType , LitStr } ;
7+ use proc_macro2:: { TokenStream as TokenStream2 , TokenTree } ;
8+ use quote:: { quote, ToTokens , TokenStreamExt } ;
9+ use syn:: {
10+ parse:: { Parse , ParseStream } ,
11+ parse_macro_input,
12+ spanned:: Spanned ,
13+ DeriveInput , Error , Generics , Ident , ItemFn , ItemType , LitStr , Visibility ,
14+ } ;
1115
1216/// Parses a type definition, extracts its identifier and generic parameters
1317struct TypeDefinition {
@@ -33,51 +37,34 @@ impl Parse for TypeDefinition {
3337 }
3438}
3539
40+ macro_rules! err {
41+ ( $span: expr, $message: expr $( , ) ?) => {
42+ Error :: new( $span. span( ) , $message) . to_compile_error( )
43+ } ;
44+ ( $span: expr, $message: expr, $( $args: expr) ,* ) => {
45+ Error :: new( $span. span( ) , format!( $message, $( $args) ,* ) ) . to_compile_error( )
46+ } ;
47+ }
48+
3649/// `unsafe_guid` attribute macro, implements the `Identify` trait for any type
3750/// (mostly works like a custom derive, but also supports type aliases)
3851#[ proc_macro_attribute]
3952pub fn unsafe_guid ( args : TokenStream , input : TokenStream ) -> TokenStream {
4053 // Parse the arguments and input using Syn
41- let guid_str = parse_macro_input ! ( args as LitStr ) . value ( ) ;
42- let mut result: proc_macro2:: TokenStream = input. clone ( ) . into ( ) ;
43- let type_definition = parse_macro_input ! ( input as TypeDefinition ) ;
54+ let ( time_low, time_mid, time_high_and_version, clock_seq_and_variant, node) =
55+ match parse_guid ( parse_macro_input ! ( args as LitStr ) ) {
56+ Ok ( data) => data,
57+ Err ( tokens) => return tokens. into ( ) ,
58+ } ;
4459
45- // We expect a canonical GUID string, such as "12345678-9abc-def0-fedc-ba9876543210"
46- if guid_str. len ( ) != 36 {
47- panic ! (
48- "\" {}\" is not a canonical GUID string (expected 36 bytes, found {})" ,
49- guid_str,
50- guid_str. len( )
51- ) ;
52- }
53- let mut guid_hex_iter = guid_str. split ( '-' ) ;
54- let mut next_guid_int = |expected_num_bits : usize | -> u64 {
55- let guid_hex_component = guid_hex_iter. next ( ) . unwrap ( ) ;
56- if guid_hex_component. len ( ) != expected_num_bits / 4 {
57- panic ! (
58- "GUID component \" {}\" is not a {}-bit hexadecimal string" ,
59- guid_hex_component, expected_num_bits
60- ) ;
61- }
62- match u64:: from_str_radix ( guid_hex_component, 16 ) {
63- Ok ( number) => number,
64- _ => panic ! (
65- "GUID component \" {}\" is not a hexadecimal number" ,
66- guid_hex_component
67- ) ,
68- }
69- } ;
60+ let mut result: TokenStream2 = input. clone ( ) . into ( ) ;
7061
71- // The GUID string is composed of a 32-bit integer, three 16-bit ones, and a 48-bit one
72- let time_low = next_guid_int ( 32 ) as u32 ;
73- let time_mid = next_guid_int ( 16 ) as u16 ;
74- let time_high_and_version = next_guid_int ( 16 ) as u16 ;
75- let clock_seq_and_variant = next_guid_int ( 16 ) as u16 ;
76- let node = next_guid_int ( 48 ) ;
62+ let type_definition = parse_macro_input ! ( input as TypeDefinition ) ;
7763
7864 // At this point, we know everything we need to implement Identify
79- let ident = type_definition. ident . clone ( ) ;
65+ let ident = & type_definition. ident ;
8066 let ( impl_generics, ty_generics, where_clause) = type_definition. generics . split_for_impl ( ) ;
67+
8168 result. append_all ( quote ! {
8269 unsafe impl #impl_generics :: uefi:: Identify for #ident #ty_generics #where_clause {
8370 #[ doc( hidden) ]
@@ -94,6 +81,61 @@ pub fn unsafe_guid(args: TokenStream, input: TokenStream) -> TokenStream {
9481 result. into ( )
9582}
9683
84+ fn parse_guid ( guid_lit : LitStr ) -> Result < ( u32 , u16 , u16 , u16 , u64 ) , TokenStream2 > {
85+ let guid_str = guid_lit. value ( ) ;
86+
87+ // We expect a canonical GUID string, such as "12345678-9abc-def0-fedc-ba9876543210"
88+ if guid_str. len ( ) != 36 {
89+ return Err ( err ! (
90+ guid_lit,
91+ "\" {}\" is not a canonical GUID string (expected 36 bytes, found {})" ,
92+ guid_str,
93+ guid_str. len( )
94+ ) ) ;
95+ }
96+ let mut offset = 1 ; // 1 is for the starting quote
97+ let mut guid_hex_iter = guid_str. split ( '-' ) ;
98+ let mut next_guid_int = |len : usize | -> Result < u64 , TokenStream2 > {
99+ let guid_hex_component = guid_hex_iter. next ( ) . unwrap ( ) ;
100+
101+ // convert syn::LitStr to proc_macro2::Literal..
102+ let lit = match guid_lit. to_token_stream ( ) . into_iter ( ) . next ( ) . unwrap ( ) {
103+ TokenTree :: Literal ( lit) => lit,
104+ _ => unreachable ! ( ) ,
105+ } ;
106+ // ..so that we can call subspan and nightly users (us) will get the fancy span
107+ let span = lit
108+ . subspan ( offset..offset + guid_hex_component. len ( ) )
109+ . unwrap_or_else ( || lit. span ( ) ) ;
110+
111+ if guid_hex_component. len ( ) != len * 2 {
112+ return Err ( err ! (
113+ span,
114+ "GUID component \" {}\" is not a {}-bit hexadecimal string" ,
115+ guid_hex_component,
116+ len * 8
117+ ) ) ;
118+ }
119+ offset += guid_hex_component. len ( ) + 1 ; // + 1 for the dash
120+ u64:: from_str_radix ( guid_hex_component, 16 ) . map_err ( |_| {
121+ err ! (
122+ span,
123+ "GUID component \" {}\" is not a hexadecimal number" ,
124+ guid_hex_component
125+ )
126+ } )
127+ } ;
128+
129+ // The GUID string is composed of a 32-bit integer, three 16-bit ones, and a 48-bit one
130+ Ok ( (
131+ next_guid_int ( 4 ) ? as u32 ,
132+ next_guid_int ( 2 ) ? as u16 ,
133+ next_guid_int ( 2 ) ? as u16 ,
134+ next_guid_int ( 2 ) ? as u16 ,
135+ next_guid_int ( 6 ) ?,
136+ ) )
137+ }
138+
97139/// Custom derive for the `Protocol` trait
98140#[ proc_macro_derive( Protocol ) ]
99141pub fn derive_protocol ( item : TokenStream ) -> TokenStream {
@@ -122,19 +164,51 @@ pub fn entry(args: TokenStream, input: TokenStream) -> TokenStream {
122164 // This code is inspired by the approach in this embedded Rust crate:
123165 // https://github.com/rust-embedded/cortex-m-rt/blob/965bf1e3291571e7e3b34834864117dc020fb391/macros/src/lib.rs#L85
124166
167+ let mut errors = TokenStream2 :: new ( ) ;
168+
125169 if !args. is_empty ( ) {
126- panic ! ( "This attribute accepts no arguments" ) ;
170+ errors. append_all ( err ! (
171+ TokenStream2 :: from( args) ,
172+ "Entry attribute accepts no arguments"
173+ ) ) ;
127174 }
128175
129176 let mut f = parse_macro_input ! ( input as ItemFn ) ;
130177
131- // force the exported symbol to be 'efi_main'
132- f. sig . ident = Ident :: new ( "efi_main" , Span :: call_site ( ) ) ;
178+ if let Some ( ref abi) = f. sig . abi {
179+ errors. append_all ( err ! ( abi, "Entry method must have no ABI modifier" ) ) ;
180+ }
181+ if let Some ( asyncness) = f. sig . asyncness {
182+ errors. append_all ( err ! ( asyncness, "Entry method should not be async" ) ) ;
183+ }
184+ if let Some ( constness) = f. sig . constness {
185+ errors. append_all ( err ! ( constness, "Entry method should not be const" ) ) ;
186+ }
187+ if !f. sig . generics . params . is_empty ( ) {
188+ errors. append_all ( err ! (
189+ & f. sig. generics. params,
190+ "Entry method should not be generic"
191+ ) ) ;
192+ }
193+
194+ // show most errors at once instead of one by one
195+ if !errors. is_empty ( ) {
196+ return errors. into ( ) ;
197+ }
198+
199+ // allow the entry function to be unsafe (by moving the keyword around so that it actually works)
200+ let unsafety = f. sig . unsafety . take ( ) ;
201+ // strip any visibility modifiers
202+ f. vis = Visibility :: Inherited ;
203+
204+ let ident = & f. sig . ident ;
133205
134206 let result = quote ! {
135- static _UEFI_ENTRY_POINT_TYPE_CHECK: extern "efiapi" fn ( uefi:: Handle , uefi:: table:: SystemTable <uefi:: table:: Boot >) -> uefi:: Status = efi_main;
136- #[ no_mangle]
137- pub extern "efiapi" #f
207+ #[ export_name = "efi_main" ]
208+ #unsafety extern "efiapi" #f
209+
210+ // typecheck the function pointer
211+ const _: #unsafety extern "efiapi" fn ( :: uefi:: Handle , :: uefi:: table:: SystemTable <:: uefi:: table:: Boot >) -> :: uefi:: Status = #ident;
138212 } ;
139213 result. into ( )
140214}
0 commit comments