diff options
Diffstat (limited to 'src/declaration.rs')
-rw-r--r-- | src/declaration.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/src/declaration.rs b/src/declaration.rs new file mode 100644 index 0000000..7543bad --- /dev/null +++ b/src/declaration.rs @@ -0,0 +1,300 @@ +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 = "illumos", + target_os = "freebsd", + ))] + extern "Rust" { + #[cfg_attr(any(target_os = "none", target_os = "linux"), 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"), 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"), 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"), 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 = "illumos", target_os = "freebsd"))] + #[cfg_attr(any(target_os = "none", target_os = "linux"), 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"), 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 = "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"), 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"), 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"), + } +} |