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