| 1 | use std::{ |
| 2 | collections::hash_map::DefaultHasher, |
| 3 | hash::{Hash as _, Hasher as _}, |
| 4 | }; |
| 5 | |
| 6 | use proc_macro::Span; |
| 7 | use proc_macro2::{Ident as Ident2, Span as Span2, TokenStream as TokenStream2}; |
| 8 | use quote::{format_ident, quote}; |
| 9 | use syn::{parse_quote, Expr, Ident, LitStr}; |
| 10 | |
| 11 | pub(crate) use symbol::mangled as mangled_symbol_name; |
| 12 | |
| 13 | mod symbol; |
| 14 | |
| 15 | pub(crate) fn crate_local_disambiguator() -> u64 { |
| 16 | // We want a deterministic, but unique-per-macro-invocation identifier. For that we |
| 17 | // hash the call site `Span`'s debug representation, which contains a counter that |
| 18 | // should disambiguate macro invocations within a crate. |
| 19 | hash(&format!(" {:?}" , Span::call_site())) |
| 20 | } |
| 21 | |
| 22 | pub(crate) fn escaped_expr_string(expr: &Expr) -> String { |
| 23 | quote!(#expr) |
| 24 | .to_string() |
| 25 | .replace('{' , "{{" ) |
| 26 | .replace(from:'}' , to:"}}" ) |
| 27 | } |
| 28 | |
| 29 | pub(crate) fn interned_string( |
| 30 | string: &str, |
| 31 | tag: &str, |
| 32 | is_log_statement: bool, |
| 33 | prefix: Option<&str>, |
| 34 | defmt_path: &syn::Path, |
| 35 | ) -> TokenStream2 { |
| 36 | // NOTE we rely on this variable name when extracting file location information from the DWARF |
| 37 | // without it we have no other mean to differentiate static variables produced by `info!` vs |
| 38 | // produced by `intern!` (or `internp`) |
| 39 | let var_name: Ident = if is_log_statement { |
| 40 | format_ident!("DEFMT_LOG_STATEMENT" ) |
| 41 | } else { |
| 42 | format_ident!("S" ) |
| 43 | }; |
| 44 | |
| 45 | let var_addr: TokenStream = if cfg!(feature = "unstable-test" ) { |
| 46 | quote!({ #defmt_path::export::fetch_add_string_index() }) |
| 47 | } else { |
| 48 | let var_item: TokenStream = static_variable(&var_name, data:string, tag, prefix); |
| 49 | quote!({ |
| 50 | #var_item |
| 51 | &#var_name as *const u8 as u16 |
| 52 | }) |
| 53 | }; |
| 54 | |
| 55 | quote!({ |
| 56 | #defmt_path::export::make_istr(#var_addr) |
| 57 | }) |
| 58 | } |
| 59 | |
| 60 | /// work around restrictions on length and allowed characters imposed by macos linker |
| 61 | /// returns (note the comma character for macos): |
| 62 | /// under macos: ".defmt," + 16 character hex digest of symbol's hash |
| 63 | /// otherwise: ".defmt." + prefix + symbol |
| 64 | pub(crate) fn linker_section(for_macos: bool, prefix: Option<&str>, symbol: &str) -> String { |
| 65 | let mut sub_section: String = if let Some(prefix: &str) = prefix { |
| 66 | format!(". {prefix}. {symbol}" ) |
| 67 | } else { |
| 68 | format!(". {symbol}" ) |
| 69 | }; |
| 70 | |
| 71 | if for_macos { |
| 72 | sub_section = format!(", {:x}" , hash(&sub_section)); |
| 73 | } |
| 74 | |
| 75 | format!(".defmt {sub_section}" ) |
| 76 | } |
| 77 | |
| 78 | pub(crate) fn static_variable( |
| 79 | name: &Ident2, |
| 80 | data: &str, |
| 81 | tag: &str, |
| 82 | prefix: Option<&str>, |
| 83 | ) -> TokenStream2 { |
| 84 | let sym_name: String = mangled_symbol_name(tag, data); |
| 85 | let section: String = linker_section(for_macos:false, prefix, &sym_name); |
| 86 | let section_for_macos: String = linker_section(for_macos:true, prefix, &sym_name); |
| 87 | |
| 88 | quote!( |
| 89 | #[cfg_attr(target_os = "macos" , link_section = #section_for_macos)] |
| 90 | #[cfg_attr(not(target_os = "macos" ), link_section = #section)] |
| 91 | #[export_name = #sym_name] |
| 92 | static #name: u8 = 0; |
| 93 | ) |
| 94 | } |
| 95 | |
| 96 | pub(crate) fn string_literal(content: &str) -> LitStr { |
| 97 | LitStr::new(value:content, span:Span2::call_site()) |
| 98 | } |
| 99 | |
| 100 | pub(crate) fn variable(name: &str) -> Expr { |
| 101 | let ident: Ident = Ident::new(string:name, span:Span2::call_site()); |
| 102 | parse_quote!(#ident) |
| 103 | } |
| 104 | |
| 105 | fn hash(string: &str) -> u64 { |
| 106 | let mut hasher: DefaultHasher = DefaultHasher::new(); |
| 107 | string.hash(&mut hasher); |
| 108 | hasher.finish() |
| 109 | } |
| 110 | |