1 use crate::{attr, linker};
2 use proc_macro2::{Span, TokenStream};
3 use quote::quote;
4 use syn::parse::{Parse, ParseStream, Result};
5 use syn::{
6     bracketed, Attribute, Error, GenericArgument, Ident, Lifetime, PathArguments, Token, Type,
7     Visibility,
8 };
9 
10 struct Declaration {
11     attrs: Vec<Attribute>,
12     vis: Visibility,
13     ident: Ident,
14     ty: Type,
15 }
16 
17 impl Parse for Declaration {
parse(input: ParseStream) -> Result<Self>18     fn parse(input: ParseStream) -> Result<Self> {
19         let attrs = input.call(Attribute::parse_outer)?;
20         let vis: Visibility = input.parse()?;
21         input.parse::<Token![static]>()?;
22         let mut_token: Option<Token![mut]> = input.parse()?;
23         if let Some(mut_token) = mut_token {
24             return Err(Error::new_spanned(
25                 mut_token,
26                 "static mut is not supported by distributed_slice",
27             ));
28         }
29         let ident: Ident = input.parse()?;
30         input.parse::<Token![:]>()?;
31         let ty: Type = input.parse()?;
32         input.parse::<Token![=]>()?;
33 
34         let content;
35         bracketed!(content in input);
36         content.parse::<Token![..]>()?;
37 
38         input.parse::<Token![;]>()?;
39 
40         Ok(Declaration {
41             attrs,
42             vis,
43             ident,
44             ty,
45         })
46     }
47 }
48 
expand(input: TokenStream) -> TokenStream49 pub fn expand(input: TokenStream) -> TokenStream {
50     let msg = "distributed_slice is not implemented for this platform";
51     let error = Error::new_spanned(&input, msg);
52     let unsupported_platform = error.to_compile_error();
53 
54     let decl: Declaration = match syn::parse2(input) {
55         Ok(decl) => decl,
56         Err(err) => return err.to_compile_error(),
57     };
58 
59     let mut attrs = decl.attrs;
60     let vis = decl.vis;
61     let ident = decl.ident;
62     let mut ty = decl.ty;
63     let name = ident.to_string();
64 
65     let linkme_path = match attr::linkme_path(&mut attrs) {
66         Ok(path) => path,
67         Err(err) => return err.to_compile_error(),
68     };
69 
70     populate_static_lifetimes(&mut ty);
71 
72     let used = if cfg!(feature = "used_linker") {
73         quote!(#[used(linker)])
74     } else {
75         quote!(#[used])
76     };
77 
78     let linux_section = linker::linux::section(&ident);
79     let linux_section_start = linker::linux::section_start(&ident);
80     let linux_section_stop = linker::linux::section_stop(&ident);
81     let linux_dupcheck = linux_section.replacen("linkme", "linkm2", 1);
82     let linux_dupcheck_start = linux_section_start.replacen("linkme", "linkm2", 1);
83     let linux_dupcheck_stop = linux_section_stop.replacen("linkme", "linkm2", 1);
84 
85     let macho_section = linker::macho::section(&ident);
86     let macho_section_start = linker::macho::section_start(&ident);
87     let macho_section_stop = linker::macho::section_stop(&ident);
88     let macho_dupcheck = macho_section.replacen("linkme", "linkm2", 1);
89     let macho_dupcheck_start = macho_section_start.replacen("linkme", "linkm2", 1);
90     let macho_dupcheck_stop = macho_section_stop.replacen("linkme", "linkm2", 1);
91 
92     let windows_section = linker::windows::section(&ident);
93     let windows_section_start = linker::windows::section_start(&ident);
94     let windows_section_stop = linker::windows::section_stop(&ident);
95     let windows_dupcheck = windows_section.replacen("linkme", "linkm2", 1);
96     let windows_dupcheck_start = windows_section_start.replacen("linkme", "linkm2", 1);
97     let windows_dupcheck_stop = windows_section_stop.replacen("linkme", "linkm2", 1);
98 
99     let illumos_section = linker::illumos::section(&ident);
100     let illumos_section_start = linker::illumos::section_start(&ident);
101     let illumos_section_stop = linker::illumos::section_stop(&ident);
102     let illumos_dupcheck = illumos_section.replacen("linkme", "linkm2", 1);
103     let illumos_dupcheck_start = illumos_section_start.replacen("linkme", "linkm2", 1);
104     let illumos_dupcheck_stop = illumos_section_stop.replacen("linkme", "linkm2", 1);
105 
106     let freebsd_section = linker::freebsd::section(&ident);
107     let freebsd_section_start = linker::freebsd::section_start(&ident);
108     let freebsd_section_stop = linker::freebsd::section_stop(&ident);
109     let freebsd_dupcheck = freebsd_section.replacen("linkme", "linkm2", 1);
110     let freebsd_dupcheck_start = freebsd_section_start.replacen("linkme", "linkm2", 1);
111     let freebsd_dupcheck_stop = freebsd_section_stop.replacen("linkme", "linkm2", 1);
112 
113     let call_site = Span::call_site();
114     let link_section_macro_str = format!("_linkme_macro_{}", ident);
115     let link_section_macro = Ident::new(&link_section_macro_str, call_site);
116 
117     quote! {
118         #(#attrs)*
119         #vis static #ident: #linkme_path::DistributedSlice<#ty> = {
120             #[cfg(any(
121                 target_os = "none",
122                 target_os = "linux",
123                 target_os = "macos",
124                 target_os = "ios",
125                 target_os = "tvos",
126                 target_os = "android",
127                 target_os = "illumos",
128                 target_os = "freebsd",
129             ))]
130             extern "Rust" {
131                 #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_section_start)]
132                 #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_start)]
133                 #[cfg_attr(target_os = "illumos", link_name = #illumos_section_start)]
134                 #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_start)]
135                 static LINKME_START: <#ty as #linkme_path::__private::Slice>::Element;
136 
137                 #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_section_stop)]
138                 #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_stop)]
139                 #[cfg_attr(target_os = "illumos", link_name = #illumos_section_stop)]
140                 #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_stop)]
141                 static LINKME_STOP: <#ty as #linkme_path::__private::Slice>::Element;
142 
143                 #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_dupcheck_start)]
144                 #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_start)]
145                 #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_start)]
146                 #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_start)]
147                 static DUPCHECK_START: #linkme_path::__private::usize;
148 
149                 #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_dupcheck_stop)]
150                 #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_stop)]
151                 #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_stop)]
152                 #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_stop)]
153                 static DUPCHECK_STOP: #linkme_path::__private::usize;
154             }
155 
156             #[cfg(target_os = "windows")]
157             #[link_section = #windows_section_start]
158             static LINKME_START: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
159 
160             #[cfg(target_os = "windows")]
161             #[link_section = #windows_section_stop]
162             static LINKME_STOP: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
163 
164             #[cfg(target_os = "windows")]
165             #[link_section = #windows_dupcheck_start]
166             static DUPCHECK_START: () = ();
167 
168             #[cfg(target_os = "windows")]
169             #[link_section = #windows_dupcheck_stop]
170             static DUPCHECK_STOP: () = ();
171 
172             #used
173             #[cfg(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "illumos", target_os = "freebsd"))]
174             #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = #linux_section)]
175             #[cfg_attr(target_os = "illumos", link_section = #illumos_section)]
176             #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)]
177             static mut LINKME_PLEASE: [<#ty as #linkme_path::__private::Slice>::Element; 0] = [];
178 
179             #used
180             #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = #linux_dupcheck)]
181             #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_dupcheck)]
182             #[cfg_attr(target_os = "windows", link_section = #windows_dupcheck)]
183             #[cfg_attr(target_os = "illumos", link_section = #illumos_dupcheck)]
184             #[cfg_attr(target_os = "freebsd", link_section = #freebsd_dupcheck)]
185             static DUPCHECK: #linkme_path::__private::usize = 1;
186 
187             #[cfg(not(any(
188                 target_os = "none",
189                 target_os = "linux",
190                 target_os = "macos",
191                 target_os = "ios",
192                 target_os = "tvos",
193                 target_os = "windows",
194                 target_os = "android",
195                 target_os = "illumos",
196                 target_os = "freebsd",
197             )))]
198             #unsupported_platform
199 
200             #linkme_path::__private::assert!(
201                 #linkme_path::__private::mem::size_of::<<#ty as #linkme_path::__private::Slice>::Element>() > 0,
202             );
203 
204             unsafe {
205                 #linkme_path::DistributedSlice::private_new(
206                     #name,
207                     &LINKME_START,
208                     &LINKME_STOP,
209                     &DUPCHECK_START,
210                     &DUPCHECK_STOP,
211                 )
212             }
213         };
214 
215         #[doc(hidden)]
216         #[macro_export]
217         macro_rules! #link_section_macro {
218             (
219                 #![linkme_macro = $macro:path]
220                 #![linkme_sort_key = $key:tt]
221                 $item:item
222             ) => {
223                 $macro ! {
224                     #![linkme_linux_section = concat!(#linux_section, $key)]
225                     #![linkme_macho_section = concat!(#macho_section, $key)]
226                     #![linkme_windows_section = concat!(#windows_section, $key)]
227                     #![linkme_illumos_section = concat!(#illumos_section, $key)]
228                     #![linkme_freebsd_section = concat!(#freebsd_section, $key)]
229                     $item
230                 }
231             };
232             (
233                 #![linkme_linux_section = $linux_section:expr]
234                 #![linkme_macho_section = $macho_section:expr]
235                 #![linkme_windows_section = $windows_section:expr]
236                 #![linkme_illumos_section = $illumos_section:expr]
237                 #![linkme_freebsd_section = $freebsd_section:expr]
238                 $item:item
239             ) => {
240                 #used
241                 #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = $linux_section)]
242                 #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = $macho_section)]
243                 #[cfg_attr(target_os = "windows", link_section = $windows_section)]
244                 #[cfg_attr(target_os = "illumos", link_section = $illumos_section)]
245                 #[cfg_attr(target_os = "freebsd", link_section = $freebsd_section)]
246                 $item
247             };
248             ($item:item) => {
249                 #used
250                 #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = #linux_section)]
251                 #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_section)]
252                 #[cfg_attr(target_os = "windows", link_section = #windows_section)]
253                 #[cfg_attr(target_os = "illumos", link_section = #illumos_section)]
254                 #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)]
255                 $item
256             };
257         }
258 
259         #[doc(hidden)]
260         #vis use #link_section_macro as #ident;
261     }
262 }
263 
populate_static_lifetimes(ty: &mut Type)264 fn populate_static_lifetimes(ty: &mut Type) {
265     match ty {
266         Type::Array(ty) => populate_static_lifetimes(&mut ty.elem),
267         Type::Group(ty) => populate_static_lifetimes(&mut ty.elem),
268         Type::Paren(ty) => populate_static_lifetimes(&mut ty.elem),
269         Type::Path(ty) => {
270             if let Some(qself) = &mut ty.qself {
271                 populate_static_lifetimes(&mut qself.ty);
272             }
273             for segment in &mut ty.path.segments {
274                 if let PathArguments::AngleBracketed(segment) = &mut segment.arguments {
275                     for arg in &mut segment.args {
276                         if let GenericArgument::Type(arg) = arg {
277                             populate_static_lifetimes(arg);
278                         }
279                     }
280                 }
281             }
282         }
283         Type::Ptr(ty) => populate_static_lifetimes(&mut ty.elem),
284         Type::Reference(ty) => {
285             if ty.lifetime.is_none() {
286                 ty.lifetime = Some(Lifetime::new("'static", ty.and_token.span));
287             }
288             populate_static_lifetimes(&mut ty.elem);
289         }
290         Type::Slice(ty) => populate_static_lifetimes(&mut ty.elem),
291         Type::Tuple(ty) => ty.elems.iter_mut().for_each(populate_static_lifetimes),
292         Type::ImplTrait(_)
293         | Type::Infer(_)
294         | Type::Macro(_)
295         | Type::Never(_)
296         | Type::TraitObject(_)
297         | Type::BareFn(_)
298         | Type::Verbatim(_) => {}
299         #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))]
300         _ => unimplemented!("unknown Type"),
301     }
302 }
303