1extern crate proc_macro;
45extern crate proc_macro2;
46#[macro_use]
47extern crate quote;
48extern crate syn;
49
50use proc_macro::TokenStream;
51use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
52use syn::{Expr, Item, ItemFn, Lit};
53
54#[proc_macro_attribute]
56pub fn constructor(args: TokenStream, input: TokenStream) -> TokenStream {
57 let item: Item = syn::parse(input).unwrap();
58
59 if let Item::Fn(ref func) = item {
60 let priority = parse_priority(args);
61
62 gen_ctor(func, priority).into()
63 } else {
64 panic!("constructor!{} is only defined for function!");
65 }
66}
67
68#[proc_macro_attribute]
70pub fn destructor(args: TokenStream, input: TokenStream) -> TokenStream {
71 let item: Item = syn::parse(input).unwrap();
72
73 if let Item::Fn(ref func) = item {
74 let priority = parse_priority(args);
75
76 gen_dtor(func, priority).into()
77 } else {
78 panic!("destructor!{} is only defined for function!");
79 }
80}
81
82fn parse_priority(args: TokenStream) -> Option<u64> {
83 if !args.is_empty() {
84 let expr: Expr = syn::parse(args).unwrap();
85
86 if let Expr::Lit(lit) = expr {
87 if let Lit::Int(n) = lit.lit {
88 return Some(n.value());
89 }
90 }
91 }
92
93 None
94}
95
96fn gen_ctor(func: &ItemFn, _priority: Option<u64>) -> TokenStream2 {
97 let mod_name = Ident::new(&format!("{}_ctor", func.ident), Span::call_site());
98 let func_name = &func.ident;
99
100 let ctor = if cfg!(target_os = "linux") {
101 quote! {
102 #[link_section = ".ctors"]
103 #[no_mangle]
104 pub static #func_name: extern fn() = super::#func_name;
105 }
106 } else if cfg!(target_os = "macos") {
107 quote! {
108 #[link_section = "__DATA,__mod_init_func"]
109 #[no_mangle]
110 pub static #func_name: extern fn() = super::#func_name;
111 }
112 } else if cfg!(target_os = "windows") {
113 quote! {
114 #[link_section = ".CRT$XCU"]
115 #[no_mangle]
116 pub static #func_name: extern fn() = super::#func_name;
117 }
118 } else {
119 unimplemented!()
120 };
121
122 quote!{
123 #func
124
125 #[doc(hidden)]
126 pub mod #mod_name {
127 #ctor
128 }
129 }
130}
131
132fn gen_dtor(func: &ItemFn, _priority: Option<u64>) -> TokenStream2 {
133 let mod_name = Ident::new(&format!("{}_dtor", func.ident), Span::call_site());
134 let func_name = &func.ident;
135 let ctor = if cfg!(target_os = "linux") {
136 quote! {
137 #[link_section = ".dtors"]
138 #[no_mangle]
139 pub static #func_name: extern fn() = super::#func_name;
140 }
141 } else if cfg!(target_os = "macos") {
142 quote! {
143 #[link_section = "__DATA,__mod_term_func"]
144 #[no_mangle]
145 pub static #func_name: extern fn() = super::#func_name;
146 }
147 } else if cfg!(target_os = "windows") {
148 quote! {
149 #[link_section = ".CRT$XPU"]
150 #[no_mangle]
151 pub static #func_name: extern fn() = super::#func_name;
152 }
153 } else {
154 unimplemented!()
155 };
156
157 quote!{
158 #func
159
160 #[doc(hidden)]
161 pub mod #mod_name {
162 #ctor
163 }
164 }
165}