• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 use proc_macro::TokenStream;
16 use proc_macro2::Ident;
17 use quote::{quote, ToTokens};
18 use syn::{
19     parse::{Parse, ParseStream},
20     parse_macro_input, Expr, Token,
21 };
22 
23 use pw_format::macros::{
24     generate_printf, Arg, CoreFmtFormatStringParser, FormatAndArgsFlavor, FormatStringParser,
25     PrintfFormatMacroGenerator, PrintfFormatStringFragment, PrintfFormatStringParser, Result,
26 };
27 
28 type TokenStream2 = proc_macro2::TokenStream;
29 
30 // Arguments to `pw_log[f]_backend`.  A log level followed by a [`pw_format`]
31 // format string.
32 #[derive(Debug)]
33 struct PwLogArgs<T: FormatStringParser> {
34     log_level: Expr,
35     format_and_args: FormatAndArgsFlavor<T>,
36 }
37 
38 impl<T: FormatStringParser> Parse for PwLogArgs<T> {
parse(input: ParseStream) -> syn::parse::Result<Self>39     fn parse(input: ParseStream) -> syn::parse::Result<Self> {
40         let log_level: Expr = input.parse()?;
41         input.parse::<Token![,]>()?;
42         let format_and_args: FormatAndArgsFlavor<_> = input.parse()?;
43 
44         Ok(PwLogArgs {
45             log_level,
46             format_and_args,
47         })
48     }
49 }
50 
51 // Generator that implements [`pw_format::PrintfFormatMacroGenerator`] to take
52 // a log line and turn it into `printf` calls;
53 struct LogfGenerator<'a> {
54     log_level: &'a Expr,
55     args: Vec<TokenStream2>,
56 }
57 
58 impl<'a> LogfGenerator<'a> {
new(log_level: &'a Expr) -> Self59     fn new(log_level: &'a Expr) -> Self {
60         Self {
61             log_level,
62             args: Vec::new(),
63         }
64     }
65 }
66 
67 // Use a [`pw_format::PrintfFormatMacroGenerator`] to prepare arguments to call
68 // `printf`.
69 impl PrintfFormatMacroGenerator for LogfGenerator<'_> {
finalize( self, format_string_fragments: &[PrintfFormatStringFragment], ) -> Result<TokenStream2>70     fn finalize(
71         self,
72         format_string_fragments: &[PrintfFormatStringFragment],
73     ) -> Result<TokenStream2> {
74         let log_level = self.log_level;
75         let args = &self.args;
76         let format_string_pieces: Vec<_> = format_string_fragments
77             .iter()
78             .map(|fragment| fragment.as_token_stream("__pw_log_backend_crate"))
79             .collect::<Result<Vec<_>>>()?;
80         Ok(quote! {
81           {
82             use core::ffi::{c_int, c_uchar};
83             use __pw_log_backend_crate::{Arguments, VarArgs};
84             // Prepend log level tag and append newline and null terminator for
85             // C string validity.
86             let format_string = __pw_log_backend_crate::concat_static_strs!(
87               "[%s] ", #(#format_string_pieces),*, "\n\0"
88             );
89             unsafe {
90               // Build up the argument type/value.
91               let args = ();
92               #(#args)*
93 
94               // Call printf through the argument type/value.
95               args.call_printf(format_string.as_ptr(),
96                 __pw_log_backend_crate::log_level_tag(#log_level).as_ptr());
97             }
98           }
99         })
100     }
101 
string_fragment(&mut self, _string: &str) -> Result<()>102     fn string_fragment(&mut self, _string: &str) -> Result<()> {
103         // String fragments are encoded directly into the format string.
104         Ok(())
105     }
106 
integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>>107     fn integer_conversion(&mut self, ty: Ident, expression: Arg) -> Result<Option<String>> {
108         self.args.push(quote! {
109           let args = <#ty as Arguments<#ty>>::push_arg(args, &((#expression) as #ty));
110         });
111         Ok(None)
112     }
113 
string_conversion(&mut self, expression: Arg) -> Result<Option<String>>114     fn string_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
115         // In order to not convert Rust Strings to CStrings at runtime, we use
116         // the "%.*s" specifier to explicitly bound the length of the
117         // non-null-terminated Rust String.
118         self.args.push(quote! {
119           let args = <&str as Arguments<&str>>::push_arg(args, &((#expression) as &str));
120         });
121         Ok(Some("%.*s".into()))
122     }
123 
char_conversion(&mut self, expression: Arg) -> Result<Option<String>>124     fn char_conversion(&mut self, expression: Arg) -> Result<Option<String>> {
125         self.args.push(quote! {
126           let args = <char as Arguments<char>>::push_arg(args, &((#expression) as char));
127         });
128         Ok(None)
129     }
130 
untyped_conversion(&mut self, expression: Arg) -> Result<()>131     fn untyped_conversion(&mut self, expression: Arg) -> Result<()> {
132         match &expression {
133             Arg::ExprCast(cast) => {
134                 let ty = &cast.ty;
135                 self.args.push(quote! {
136                   let args = <#ty as Arguments<#ty>>::push_arg(args, &(#expression));
137                 });
138             }
139             Arg::Expr(_) => {
140                 return Err(pw_format::macros::Error::new(&format!(
141                 "Expected argument to untyped format (%v) to be a cast expression (e.g. x as i32), but found {}.",
142                 expression.to_token_stream()
143               )));
144             }
145         }
146         Ok(())
147     }
148 }
149 
150 #[proc_macro]
_pw_log_backend(tokens: TokenStream) -> TokenStream151 pub fn _pw_log_backend(tokens: TokenStream) -> TokenStream {
152     let input = parse_macro_input!(tokens as PwLogArgs<CoreFmtFormatStringParser>);
153     let generator = LogfGenerator::new(&input.log_level);
154 
155     match generate_printf(generator, input.format_and_args.into()) {
156         Ok(token_stream) => token_stream.into(),
157         Err(e) => e.to_compile_error().into(),
158     }
159 }
160 
161 #[proc_macro]
_pw_logf_backend(tokens: TokenStream) -> TokenStream162 pub fn _pw_logf_backend(tokens: TokenStream) -> TokenStream {
163     let input = parse_macro_input!(tokens as PwLogArgs<PrintfFormatStringParser>);
164     let generator = LogfGenerator::new(&input.log_level);
165 
166     match generate_printf(generator, input.format_and_args.into()) {
167         Ok(token_stream) => token_stream.into(),
168         Err(e) => e.to_compile_error().into(),
169     }
170 }
171