| use crate::{attr, linker}; |
| use proc_macro2::{Span, TokenStream}; |
| use quote::quote; |
| use syn::parse::{Parse, ParseStream, Result}; |
| use syn::{ |
| bracketed, Attribute, Error, GenericArgument, Ident, Lifetime, PathArguments, Token, Type, |
| Visibility, |
| }; |
| |
| struct Declaration { |
| attrs: Vec<Attribute>, |
| vis: Visibility, |
| ident: Ident, |
| ty: Type, |
| } |
| |
| impl Parse for Declaration { |
| fn parse(input: ParseStream) -> Result<Self> { |
| let attrs = input.call(Attribute::parse_outer)?; |
| let vis: Visibility = input.parse()?; |
| input.parse::<Token![static]>()?; |
| let mut_token: Option<Token![mut]> = input.parse()?; |
| if let Some(mut_token) = mut_token { |
| return Err(Error::new_spanned( |
| mut_token, |
| "static mut is not supported by distributed_slice", |
| )); |
| } |
| let ident: Ident = input.parse()?; |
| input.parse::<Token![:]>()?; |
| let ty: Type = input.parse()?; |
| input.parse::<Token![=]>()?; |
| |
| let content; |
| bracketed!(content in input); |
| content.parse::<Token![..]>()?; |
| |
| input.parse::<Token![;]>()?; |
| |
| Ok(Declaration { |
| attrs, |
| vis, |
| ident, |
| ty, |
| }) |
| } |
| } |
| |
| pub fn expand(input: TokenStream) -> TokenStream { |
| let msg = "distributed_slice is not implemented for this platform"; |
| let error = Error::new_spanned(&input, msg); |
| let unsupported_platform = error.to_compile_error(); |
| |
| let decl: Declaration = match syn::parse2(input) { |
| Ok(decl) => decl, |
| Err(err) => return err.to_compile_error(), |
| }; |
| |
| let mut attrs = decl.attrs; |
| let vis = decl.vis; |
| let ident = decl.ident; |
| let mut ty = decl.ty; |
| let name = ident.to_string(); |
| |
| let linkme_path = match attr::linkme_path(&mut attrs) { |
| Ok(path) => path, |
| Err(err) => return err.to_compile_error(), |
| }; |
| |
| populate_static_lifetimes(&mut ty); |
| |
| let used = if cfg!(feature = "used_linker") { |
| quote!(#[used(linker)]) |
| } else { |
| quote!(#[used]) |
| }; |
| |
| let linux_section = linker::linux::section(&ident); |
| let linux_section_start = linker::linux::section_start(&ident); |
| let linux_section_stop = linker::linux::section_stop(&ident); |
| let linux_dupcheck = linux_section.replacen("linkme", "linkm2", 1); |
| let linux_dupcheck_start = linux_section_start.replacen("linkme", "linkm2", 1); |
| let linux_dupcheck_stop = linux_section_stop.replacen("linkme", "linkm2", 1); |
| |
| let macho_section = linker::macho::section(&ident); |
| let macho_section_start = linker::macho::section_start(&ident); |
| let macho_section_stop = linker::macho::section_stop(&ident); |
| let macho_dupcheck = macho_section.replacen("linkme", "linkm2", 1); |
| let macho_dupcheck_start = macho_section_start.replacen("linkme", "linkm2", 1); |
| let macho_dupcheck_stop = macho_section_stop.replacen("linkme", "linkm2", 1); |
| |
| let windows_section = linker::windows::section(&ident); |
| let windows_section_start = linker::windows::section_start(&ident); |
| let windows_section_stop = linker::windows::section_stop(&ident); |
| let windows_dupcheck = windows_section.replacen("linkme", "linkm2", 1); |
| let windows_dupcheck_start = windows_section_start.replacen("linkme", "linkm2", 1); |
| let windows_dupcheck_stop = windows_section_stop.replacen("linkme", "linkm2", 1); |
| |
| let illumos_section = linker::illumos::section(&ident); |
| let illumos_section_start = linker::illumos::section_start(&ident); |
| let illumos_section_stop = linker::illumos::section_stop(&ident); |
| let illumos_dupcheck = illumos_section.replacen("linkme", "linkm2", 1); |
| let illumos_dupcheck_start = illumos_section_start.replacen("linkme", "linkm2", 1); |
| let illumos_dupcheck_stop = illumos_section_stop.replacen("linkme", "linkm2", 1); |
| |
| let freebsd_section = linker::freebsd::section(&ident); |
| let freebsd_section_start = linker::freebsd::section_start(&ident); |
| let freebsd_section_stop = linker::freebsd::section_stop(&ident); |
| let freebsd_dupcheck = freebsd_section.replacen("linkme", "linkm2", 1); |
| let freebsd_dupcheck_start = freebsd_section_start.replacen("linkme", "linkm2", 1); |
| let freebsd_dupcheck_stop = freebsd_section_stop.replacen("linkme", "linkm2", 1); |
| |
| let call_site = Span::call_site(); |
| let link_section_macro_str = format!("_linkme_macro_{}", ident); |
| let link_section_macro = Ident::new(&link_section_macro_str, call_site); |
| |
| quote! { |
| #(#attrs)* |
| #vis static #ident: #linkme_path::DistributedSlice<#ty> = { |
| #[cfg(any( |
| target_os = "none", |
| target_os = "linux", |
| target_os = "macos", |
| target_os = "ios", |
| target_os = "tvos", |
| target_os = "android", |
| target_os = "illumos", |
| target_os = "freebsd", |
| ))] |
| extern "Rust" { |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_section_start)] |
| #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_start)] |
| #[cfg_attr(target_os = "illumos", link_name = #illumos_section_start)] |
| #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_start)] |
| static LINKME_START: <#ty as #linkme_path::__private::Slice>::Element; |
| |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_section_stop)] |
| #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section_stop)] |
| #[cfg_attr(target_os = "illumos", link_name = #illumos_section_stop)] |
| #[cfg_attr(target_os = "freebsd", link_name = #freebsd_section_stop)] |
| static LINKME_STOP: <#ty as #linkme_path::__private::Slice>::Element; |
| |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_dupcheck_start)] |
| #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_start)] |
| #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_start)] |
| #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_start)] |
| static DUPCHECK_START: #linkme_path::__private::usize; |
| |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_name = #linux_dupcheck_stop)] |
| #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_dupcheck_stop)] |
| #[cfg_attr(target_os = "illumos", link_name = #illumos_dupcheck_stop)] |
| #[cfg_attr(target_os = "freebsd", link_name = #freebsd_dupcheck_stop)] |
| static DUPCHECK_STOP: #linkme_path::__private::usize; |
| } |
| |
| #[cfg(target_os = "windows")] |
| #[link_section = #windows_section_start] |
| static LINKME_START: [<#ty as #linkme_path::__private::Slice>::Element; 0] = []; |
| |
| #[cfg(target_os = "windows")] |
| #[link_section = #windows_section_stop] |
| static LINKME_STOP: [<#ty as #linkme_path::__private::Slice>::Element; 0] = []; |
| |
| #[cfg(target_os = "windows")] |
| #[link_section = #windows_dupcheck_start] |
| static DUPCHECK_START: () = (); |
| |
| #[cfg(target_os = "windows")] |
| #[link_section = #windows_dupcheck_stop] |
| static DUPCHECK_STOP: () = (); |
| |
| #used |
| #[cfg(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "illumos", target_os = "freebsd"))] |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = #linux_section)] |
| #[cfg_attr(target_os = "illumos", link_section = #illumos_section)] |
| #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)] |
| static mut LINKME_PLEASE: [<#ty as #linkme_path::__private::Slice>::Element; 0] = []; |
| |
| #used |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = #linux_dupcheck)] |
| #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_dupcheck)] |
| #[cfg_attr(target_os = "windows", link_section = #windows_dupcheck)] |
| #[cfg_attr(target_os = "illumos", link_section = #illumos_dupcheck)] |
| #[cfg_attr(target_os = "freebsd", link_section = #freebsd_dupcheck)] |
| static DUPCHECK: #linkme_path::__private::usize = 1; |
| |
| #[cfg(not(any( |
| target_os = "none", |
| target_os = "linux", |
| target_os = "macos", |
| target_os = "ios", |
| target_os = "tvos", |
| target_os = "windows", |
| target_os = "android", |
| target_os = "illumos", |
| target_os = "freebsd", |
| )))] |
| #unsupported_platform |
| |
| #linkme_path::__private::assert!( |
| #linkme_path::__private::mem::size_of::<<#ty as #linkme_path::__private::Slice>::Element>() > 0, |
| ); |
| |
| unsafe { |
| #linkme_path::DistributedSlice::private_new( |
| #name, |
| &LINKME_START, |
| &LINKME_STOP, |
| &DUPCHECK_START, |
| &DUPCHECK_STOP, |
| ) |
| } |
| }; |
| |
| #[doc(hidden)] |
| #[macro_export] |
| macro_rules! #link_section_macro { |
| ( |
| #![linkme_macro = $macro:path] |
| #![linkme_sort_key = $key:tt] |
| $item:item |
| ) => { |
| $macro ! { |
| #![linkme_linux_section = concat!(#linux_section, $key)] |
| #![linkme_macho_section = concat!(#macho_section, $key)] |
| #![linkme_windows_section = concat!(#windows_section, $key)] |
| #![linkme_illumos_section = concat!(#illumos_section, $key)] |
| #![linkme_freebsd_section = concat!(#freebsd_section, $key)] |
| $item |
| } |
| }; |
| ( |
| #![linkme_linux_section = $linux_section:expr] |
| #![linkme_macho_section = $macho_section:expr] |
| #![linkme_windows_section = $windows_section:expr] |
| #![linkme_illumos_section = $illumos_section:expr] |
| #![linkme_freebsd_section = $freebsd_section:expr] |
| $item:item |
| ) => { |
| #used |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = $linux_section)] |
| #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = $macho_section)] |
| #[cfg_attr(target_os = "windows", link_section = $windows_section)] |
| #[cfg_attr(target_os = "illumos", link_section = $illumos_section)] |
| #[cfg_attr(target_os = "freebsd", link_section = $freebsd_section)] |
| $item |
| }; |
| ($item:item) => { |
| #used |
| #[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android"), link_section = #linux_section)] |
| #[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_section = #macho_section)] |
| #[cfg_attr(target_os = "windows", link_section = #windows_section)] |
| #[cfg_attr(target_os = "illumos", link_section = #illumos_section)] |
| #[cfg_attr(target_os = "freebsd", link_section = #freebsd_section)] |
| $item |
| }; |
| } |
| |
| #[doc(hidden)] |
| #vis use #link_section_macro as #ident; |
| } |
| } |
| |
| fn populate_static_lifetimes(ty: &mut Type) { |
| match ty { |
| Type::Array(ty) => populate_static_lifetimes(&mut ty.elem), |
| Type::Group(ty) => populate_static_lifetimes(&mut ty.elem), |
| Type::Paren(ty) => populate_static_lifetimes(&mut ty.elem), |
| Type::Path(ty) => { |
| if let Some(qself) = &mut ty.qself { |
| populate_static_lifetimes(&mut qself.ty); |
| } |
| for segment in &mut ty.path.segments { |
| if let PathArguments::AngleBracketed(segment) = &mut segment.arguments { |
| for arg in &mut segment.args { |
| if let GenericArgument::Type(arg) = arg { |
| populate_static_lifetimes(arg); |
| } |
| } |
| } |
| } |
| } |
| Type::Ptr(ty) => populate_static_lifetimes(&mut ty.elem), |
| Type::Reference(ty) => { |
| if ty.lifetime.is_none() { |
| ty.lifetime = Some(Lifetime::new("'static", ty.and_token.span)); |
| } |
| populate_static_lifetimes(&mut ty.elem); |
| } |
| Type::Slice(ty) => populate_static_lifetimes(&mut ty.elem), |
| Type::Tuple(ty) => ty.elems.iter_mut().for_each(populate_static_lifetimes), |
| Type::ImplTrait(_) |
| | Type::Infer(_) |
| | Type::Macro(_) |
| | Type::Never(_) |
| | Type::TraitObject(_) |
| | Type::BareFn(_) |
| | Type::Verbatim(_) => {} |
| #[cfg_attr(all(test, exhaustive), deny(non_exhaustive_omitted_patterns))] |
| _ => unimplemented!("unknown Type"), |
| } |
| } |