Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement disjointed statics #85

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 31 additions & 5 deletions impl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,51 @@

mod args;
mod attr;
mod declaration;
mod element;
mod hash;
mod linker;
mod singleton;
mod slice;
mod ty;

use crate::args::Args;
use crate::hash::hash;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, quote_spanned};
use syn::parse_macro_input;
use syn::spanned::Spanned;

#[proc_macro_attribute]
pub fn distributed_slice(args: TokenStream, input: TokenStream) -> TokenStream {
let args = parse_macro_input!(args as Args);

let expanded = match args {
Args::None => declaration::expand(parse_macro_input!(input)),
Args::Path(path) => element::expand(path, None, parse_macro_input!(input)),
Args::PathPos(path, pos) => element::expand(path, pos, parse_macro_input!(input)),
Args::None => slice::declaration::expand(parse_macro_input!(input)),
Args::Path(path) => slice::element::expand(path, None, parse_macro_input!(input)),
Args::PathPos(path, pos) => slice::element::expand(path, pos, parse_macro_input!(input)),
};

TokenStream::from(expanded)
}

#[proc_macro_attribute]
pub fn disjointed_static(args: TokenStream, input: TokenStream) -> TokenStream {
let args2: TokenStream2 = args.clone().into();
let args = parse_macro_input!(args as Args);

let expanded = match args {
Args::None => singleton::declaration::expand(parse_macro_input!(input)),
Args::Path(path) => singleton::item::expand(path, parse_macro_input!(input)),
Args::PathPos(path, _) => {
let sort = quote_spanned! {args2.span()=>
compile_error!("disjointed_static does not accept a sort key");
};
let item = singleton::item::expand(path, parse_macro_input!(input));
quote! {
#sort
#item
}
}
};

TokenStream::from(expanded)
Expand Down
211 changes: 211 additions & 0 deletions impl/src/singleton/declaration.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
use crate::{attr, linker, ty};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::parse::{Parse, ParseStream, Result};
use syn::{Attribute, Error, Ident, 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 disjointed_static",
));
}
let ident: Ident = input.parse()?;
input.parse::<Token![:]>()?;
let ty: Type = input.parse()?;

let eq_token: Option<Token![=]> = input.parse()?;
if eq_token.is_some() {
input.parse::<Token![..]>()?;
}

input.parse::<Token![;]>()?;

Ok(Declaration {
attrs,
vis,
ident,
ty,
})
}
}

pub fn expand(input: TokenStream) -> TokenStream {
let msg = "disjointed_static 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(),
};

ty::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::DisjointedStatic<#ty> = {
#[cfg(any(
target_os = "none",
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "tvos",
target_os = "windows",
target_os = "android",
target_os = "fuchsia",
target_os = "illumos",
target_os = "freebsd",
target_os = "psp",
))]
extern "Rust" {
#[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), link_name = #linux_section)]
#[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), link_name = #macho_section)]
#[cfg_attr(target_os = "windows", link_name = #windows_section)]
#[cfg_attr(target_os = "illumos", link_name = #illumos_section)]
#[cfg_attr(target_os = "freebsd", link_name = #freebsd_section)]
static LINKME_SINGLETON: #ty;

#[cfg(not(target_os = "windows"))]
#[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), 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(not(target_os = "windows"))]
#[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), 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_dupcheck_start]
static DUPCHECK_START: () = ();

#[cfg(target_os = "windows")]
#[link_section = #windows_dupcheck_stop]
static DUPCHECK_STOP: () = ();

#used
#[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), 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 = "fuchsia",
target_os = "illumos",
target_os = "freebsd",
target_os = "psp",
)))]
#unsupported_platform

unsafe {
#linkme_path::DisjointedStatic::private_new(
#name,
#linkme_path::__private::ptr::addr_of!(LINKME_SINGLETON),
#linkme_path::__private::ptr::addr_of!(DUPCHECK_START),
#linkme_path::__private::ptr::addr_of!(DUPCHECK_STOP),
)
}
};

#[doc(hidden)]
#[macro_export]
macro_rules! #link_section_macro {
($item:item) => {
#used
#[cfg_attr(any(target_os = "none", target_os = "linux", target_os = "android", target_os = "fuchsia", target_os = "psp"), export_name = #linux_section)]
#[cfg_attr(any(target_os = "macos", target_os = "ios", target_os = "tvos"), export_name = #macho_section)]
#[cfg_attr(target_os = "windows", export_name = #windows_section)]
#[cfg_attr(target_os = "illumos", export_name = #illumos_section)]
#[cfg_attr(target_os = "freebsd", export_name = #freebsd_section)]
$item
};
}

#[doc(hidden)]
#vis use #link_section_macro as #ident;
}
}
90 changes: 90 additions & 0 deletions impl/src/singleton/item.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
use crate::{attr, ty};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::parse::{Error, Parse, ParseStream, Result};
use syn::{Attribute, Ident, Path, Token, Type, Visibility};

pub struct Item {
attrs: Vec<Attribute>,
vis: Visibility,
ident: Ident,
ty: Type,
expr: TokenStream,
orig_item: Option<TokenStream>,
start_span: Span,
end_span: Span,
}

impl Parse for Item {
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 disjointed_static",
));
}
let ident: Ident = input.parse()?;
input.parse::<Token![:]>()?;
let start_span = input.span();
let ty: Type = input.parse()?;
let end_span = quote!(#ty).into_iter().last().unwrap().span();
input.parse::<Token![=]>()?;
let mut expr_semi = Vec::from_iter(input.parse::<TokenStream>()?);
if let Some(tail) = expr_semi.pop() {
syn::parse2::<Token![;]>(TokenStream::from(tail))?;
}
let expr = TokenStream::from_iter(expr_semi);
Ok(Item {
attrs,
vis,
ident,
ty,
expr,
orig_item: None,
start_span,
end_span,
})
}
}

pub fn expand(path: Path, input: Item) -> TokenStream {
let mut attrs = input.attrs;
let vis = input.vis;
let ident = input.ident;
let mut ty = input.ty;
let expr = input.expr;
let orig_item = input.orig_item;

ty::populate_static_lifetimes(&mut ty);

let linkme_path = match attr::linkme_path(&mut attrs) {
Ok(path) => path,
Err(err) => return err.to_compile_error(),
};

let factory = quote_spanned!(input.start_span=> __new);
let get = quote_spanned!(input.end_span=> #factory());

quote! {
#path ! {
#(#attrs)*
#vis static #ident : #ty = {
#[allow(clippy::no_effect_underscore_binding)]
unsafe fn __typecheck(_: #linkme_path::__private::Void) {
let #factory = || -> fn() -> &'static #ty { || &#ident };
unsafe {
#linkme_path::DisjointedStatic::private_typecheck(#path, #get);
}
}

#expr
};
}

#orig_item
}
}
2 changes: 2 additions & 0 deletions impl/src/singleton/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod declaration;
pub mod item;
File renamed without changes.
File renamed without changes.
2 changes: 2 additions & 0 deletions impl/src/slice/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub mod declaration;
pub mod element;
Loading
Loading