use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
fn get_function_name(stream: TokenStream) -> String {
let mut iter = stream.into_iter();
macro_rules! unexpected {
() => {
panic!("#[ctor] can only be applied to unsafe functions")
};
}
macro_rules! expect_ident {
() => {
match iter.next() {
Some(TokenTree::Ident(ident)) => ident,
_ => unexpected!(),
}
};
}
while let Some(token) = iter.next() {
if let TokenTree::Ident(ident) = token {
if ident.to_string() != "unsafe" || expect_ident!().to_string() != "fn" {
unexpected!()
}
return expect_ident!().to_string();
}
}
unexpected!();
}
macro_rules! tokens {
($($expr:expr),* $(,)?) => {
vec![$($expr,)*].into_iter().collect::<TokenStream>()
}
}
#[proc_macro_attribute]
pub fn ctor(args: TokenStream, input: TokenStream) -> TokenStream {
if args.into_iter().next().is_some() {
panic!("#[ctor] takes no arguments");
}
let name = get_function_name(input.clone());
let ctor_ident = TokenTree::Ident(Ident::new(
&format!("___{}___ctor", name),
Span::call_site(),
));
vec![
TokenTree::Punct(Punct::new('#', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
tokens![TokenTree::Ident(Ident::new("used", Span::call_site()))],
)),
TokenTree::Punct(Punct::new('#', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
tokens![
TokenTree::Ident(Ident::new("doc", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
vec![TokenTree::Ident(Ident::new("hidden", Span::call_site()))]
.into_iter()
.collect(),
)),
],
)),
TokenTree::Punct(Punct::new('#', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
tokens![
TokenTree::Ident(Ident::new("allow", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
tokens![TokenTree::Ident(Ident::new(
"non_upper_case_globals",
Span::call_site(),
))]
)),
],
)),
TokenTree::Punct(Punct::new('#', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
tokens![
TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
tokens![
TokenTree::Ident(Ident::new("any", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
tokens![
TokenTree::Ident(Ident::new("target_os", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("linux")),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new("target_os", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("freebsd")),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new("target_os", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("netbsd")),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new("target_os", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("android")),
],
)),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new("link_section", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string(".init_array")),
],
))
],
)),
TokenTree::Punct(Punct::new('#', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
tokens![
TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
tokens![
TokenTree::Ident(Ident::new("target_vendor", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("apple")),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new("link_section", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("__DATA,__mod_init_func")),
],
))
],
)),
TokenTree::Punct(Punct::new('#', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
tokens![
TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
tokens![
TokenTree::Ident(Ident::new("target_os", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("windows")),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new("link_section", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string(".CRT$XCU")),
],
))
],
)),
TokenTree::Ident(Ident::new("static", Span::call_site())),
ctor_ident.clone(),
TokenTree::Punct(Punct::new(':', Spacing::Alone)),
TokenTree::Ident(Ident::new("unsafe", Span::call_site())),
TokenTree::Ident(Ident::new("extern", Span::call_site())),
TokenTree::Literal(Literal::string("C")),
TokenTree::Ident(Ident::new("fn", Span::call_site())),
TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::default())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Brace,
tokens![
TokenTree::Punct(Punct::new('#', Spacing::Alone)),
TokenTree::Group(Group::new(
Delimiter::Bracket,
tokens![
TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
tokens![
TokenTree::Ident(Ident::new("any", Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
tokens![
TokenTree::Ident(Ident::new(
"target_os",
Span::call_site()
)),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("linux")),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new(
"target_os",
Span::call_site()
)),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string("android")),
],
)),
TokenTree::Punct(Punct::new(',', Spacing::Alone)),
TokenTree::Ident(Ident::new("link_section", Span::call_site())),
TokenTree::Punct(Punct::new('=', Spacing::Alone)),
TokenTree::Literal(Literal::string(".text.startup")),
],
))
],
)),
TokenTree::Ident(Ident::new("unsafe", Span::call_site())),
TokenTree::Ident(Ident::new("extern", Span::call_site())),
TokenTree::Literal(Literal::string("C")),
TokenTree::Ident(Ident::new("fn", Span::call_site())),
ctor_ident.clone(),
TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::default())),
TokenTree::Group(Group::new(
Delimiter::Brace,
vec![
TokenTree::Ident(Ident::new(&name, Span::call_site())),
TokenTree::Group(Group::new(
Delimiter::Parenthesis,
TokenStream::default(),
)),
]
.into_iter()
.collect(),
)),
ctor_ident.clone(),
],
)),
TokenTree::Punct(Punct::new(';', Spacing::Alone)),
]
.into_iter()
.chain(input.into_iter())
.collect()
}