#![recursion_limit = "256"]
extern crate proc_macro;
extern crate syn;
#[macro_use]
extern crate quote;
use proc_macro::TokenStream;
#[proc_macro_attribute]
pub fn ctor(_attribute: TokenStream, function: TokenStream) -> TokenStream {
let item: syn::Item = syn::parse_macro_input!(function);
if let syn::Item::Fn(function) = item {
validate_item("ctor", &function);
let syn::ItemFn {
attrs,
block,
sig:
syn::Signature {
ident,
unsafety,
constness,
abi,
..
},
..
} = function;
let output = quote!(
#[used]
#[allow(non_upper_case_globals)]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
#[cfg_attr(windows, link_section = ".CRT$XCU")]
#(#attrs)*
static #ident
:
#unsafety extern #abi #constness fn() =
{
#[cfg_attr(target_os = "linux", link_section = ".text.startup")]
#unsafety extern #abi #constness fn #ident() #block;
#ident
}
;
);
output.into()
} else if let syn::Item::Static(var) = item {
let syn::ItemStatic {
ident,
mutability,
expr,
attrs,
ty,
vis,
..
} = var;
if let Some(_) = mutability {
panic!("#[ctor]-annotated static objects must not be mutable");
}
if attrs.iter().any(|attr| {
attr.path
.segments
.iter()
.any(|segment| segment.ident == "no_mangle")
}) {
panic!("#[ctor]-annotated static objects do not support #[no_mangle]");
}
let ctor_ident =
syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___ctor", ident).as_ref())
.expect("Unable to create identifier");
let storage_ident =
syn::parse_str::<syn::Ident>(format!("{}___rust_ctor___storage", ident).as_ref())
.expect("Unable to create identifier");
let output = quote!(
static mut #storage_ident: Option<#ty> = None;
#[doc(hidden)]
#[allow(non_camel_case_types)]
#vis struct #ident<T> {
_data: core::marker::PhantomData<T>
}
#(#attrs)*
#vis static #ident: #ident<#ty> = #ident {
_data: core::marker::PhantomData::<#ty>
};
impl core::ops::Deref for #ident<#ty> {
type Target = #ty;
fn deref(&self) -> &'static #ty {
unsafe {
#storage_ident.as_ref().unwrap()
}
}
}
#[used]
#[allow(non_upper_case_globals)]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
#[cfg_attr(windows, link_section = ".CRT$XCU")]
static #ctor_ident
:
unsafe fn() = {
#[cfg_attr(target_os = "linux", link_section = ".text.startup")]
unsafe fn initer() {
#storage_ident = Some(#expr);
}; initer }
;
);
output.into()
} else {
panic!("#[ctor] items must be functions or static globals");
}
}
#[proc_macro_attribute]
pub fn dtor(_attribute: TokenStream, function: TokenStream) -> TokenStream {
let function: syn::ItemFn = syn::parse_macro_input!(function);
validate_item("dtor", &function);
let syn::ItemFn {
attrs,
block,
sig:
syn::Signature {
ident,
unsafety,
constness,
abi,
..
},
..
} = function;
let output = quote!(
mod #ident {
use super::*;
extern "C" {
fn atexit(cb: #unsafety extern fn());
}
#[used]
#[allow(non_upper_case_globals)]
#[cfg_attr(target_os = "linux", link_section = ".init_array")]
#[cfg_attr(target_os = "freebsd", link_section = ".init_array")]
#[cfg_attr(target_os = "macos", link_section = "__DATA,__mod_init_func")]
#[cfg_attr(windows, link_section = ".CRT$XCU")]
#(#attrs)*
static __dtor_export
:
unsafe extern #abi #constness fn() =
{
#[cfg_attr(target_os = "linux", link_section = ".text.exit")]
#unsafety extern #abi #constness fn #ident() #block;
#[cfg_attr(target_os = "linux", link_section = ".text.startup")]
unsafe extern fn __dtor_atexit() {
atexit(#ident);
};
__dtor_atexit
};
}
);
output.into()
}
fn validate_item(typ: &str, item: &syn::ItemFn) {
let syn::ItemFn { vis, sig, .. } = item;
match vis {
syn::Visibility::Inherited => {}
_ => panic!("#[{}] methods must not have visibility modifiers", typ),
}
if sig.inputs.len() > 0 {
panic!("#[{}] methods may not have parameters", typ);
}
match sig.output {
syn::ReturnType::Default => {}
_ => panic!("#[{}] methods must not have return types", typ),
}
}