xref: /aosp_15_r20/external/cronet/third_party/rust/chromium_crates_io/vendor/small_ctor-0.1.1/src/lib.rs (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 //! A dependency free library that declares functions to be automatically
2 //! executed before `main` is invoked.
3 //!
4 //! This crate implements the exact same behavior as the
5 //! [`ctor`](https://crates.io/crates/ctor) crate with the following
6 //! differences:
7 //!
8 //! * It has no dependencies other than `proc_macro` itself.
9 //! * It requires that functions are marked with `unsafe`.
10 //! * It can only be used with functions, not static items.
11 //! * It only supports `#[ctor]`
12 //!
13 //! ## Example
14 //!
15 //! This is a motivating example that registers a struct as a plugin in a
16 //! hypothetical global plugins registry:
17 //!
18 //! ```
19 //! struct MyPlugin;
20 //! # impl MyPlugin { fn register(&self, _: MyPlugin) {} }
21 //! # static PLUGINS: MyPlugin = MyPlugin;
22 //!
23 //! #[small_ctor::ctor]
24 //! unsafe fn register_plugin() {
25 //!     PLUGINS.register(MyPlugin);
26 //! }
27 //! ```
28 //!
29 //! ## Safety
30 //!
31 //! This library involves "life before main" which is explicitly not permitted
32 //! in Rust which is why this library is anything but safe.  In fact, it's a
33 //! really bad idea to do what this crate is promoting.  For instance at
34 //! present code that runs in `#[ctor]` will run before `lang_start` managed to
35 //! execute.  Some of the effects of this are that the main thread does not have
36 //! a name yet, the stack protection is not enabled and code must not panic.
37 //!
38 //! It's recommended that the only thing you do in a `#[ctor]` function is to
39 //! append to a vector, insert into a hashmap or similar.
40 //!
41 //! ## Recommended Usage
42 //!
43 //! It's recommended to perform basic operations which are unlikely to panic
44 //! and to defer most work to when the actual `main` happens.  So for
45 //! instead instead of initializing plugins in the `ctor`, just push them
46 //! to a plugin registry and then trigger callbacks registered that way
47 //! regularly in `main`.
48 //!
49 //! ## Compiler and Linker Bugs
50 //!
51 //! Currently this library is prone to break due to compiler bugs in subtle
52 //! ways.  The core issue is [rust #47384](https://github.com/rust-lang/rust/issues/47384).
53 //! You can reduze the likelihood of it happening by disabling incremental
54 //! compilation or setting `codegen-units` to `1` in your profile in the
55 //! `Cargo.toml`.
56 //!
57 //! ## Destructors
58 //!
59 //! This crate intentionally does not support an at-exit mechanism.  The reason
60 //! for this is that those are running so late that even more Rust code is
61 //! unable to properly run.  Not only does panicking not work, the entire standard
62 //! IO system is already unusable.  More importantly on many platforms these
63 //! do not run properly.  For instance on macOS destructors do not run when
64 //! thread local storage is in use.  If you do want to use something like this
65 //! you can do something like invoke `libc::at_exit` from within a `#[ctor]`
66 //! function.
67 use proc_macro::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
68 
get_function_name(stream: TokenStream) -> String69 fn get_function_name(stream: TokenStream) -> String {
70     let mut iter = stream.into_iter();
71 
72     macro_rules! unexpected {
73         () => {
74             panic!("#[ctor] can only be applied to unsafe functions")
75         };
76     }
77 
78     macro_rules! expect_ident {
79         () => {
80             match iter.next() {
81                 Some(TokenTree::Ident(ident)) => ident,
82                 _ => unexpected!(),
83             }
84         };
85     }
86 
87     while let Some(token) = iter.next() {
88         if let TokenTree::Ident(ident) = token {
89             if ident.to_string() != "unsafe" || expect_ident!().to_string() != "fn" {
90                 unexpected!()
91             }
92             return expect_ident!().to_string();
93         }
94     }
95 
96     unexpected!();
97 }
98 
99 macro_rules! tokens {
100     ($($expr:expr),* $(,)?) => {
101         vec![$($expr,)*].into_iter().collect::<TokenStream>()
102     }
103 }
104 
105 /// Marks a function or static variable as a library/executable constructor.
106 /// This uses OS-specific linker sections to call a specific function at load
107 /// time.
108 ///
109 /// Multiple startup functions/statics are supported, but the invocation order
110 /// is not guaranteed.  For information about what is safe or not safe to do
111 /// in such functions refer to the module documention.
112 ///
113 /// # Example
114 ///
115 /// ```
116 /// # struct MyPlugin;
117 /// # impl MyPlugin { fn insert(&self, _: MyPlugin) {} }
118 /// #[small_ctor::ctor]
119 /// unsafe fn register_plugin() {
120 /// # let PLUGINS = MyPlugin;
121 ///     PLUGINS.insert(MyPlugin);
122 /// }
123 /// ```
124 #[proc_macro_attribute]
ctor(args: TokenStream, input: TokenStream) -> TokenStream125 pub fn ctor(args: TokenStream, input: TokenStream) -> TokenStream {
126     if args.into_iter().next().is_some() {
127         panic!("#[ctor] takes no arguments");
128     }
129     let name = get_function_name(input.clone());
130     let ctor_ident = TokenTree::Ident(Ident::new(
131         &format!("___{}___ctor", name),
132         Span::call_site(),
133     ));
134     vec![
135         TokenTree::Punct(Punct::new('#', Spacing::Alone)),
136         TokenTree::Group(Group::new(
137             Delimiter::Bracket,
138             tokens![TokenTree::Ident(Ident::new("used", Span::call_site()))],
139         )),
140         TokenTree::Punct(Punct::new('#', Spacing::Alone)),
141         TokenTree::Group(Group::new(
142             Delimiter::Bracket,
143             tokens![
144                 TokenTree::Ident(Ident::new("doc", Span::call_site())),
145                 TokenTree::Group(Group::new(
146                     Delimiter::Parenthesis,
147                     vec![TokenTree::Ident(Ident::new("hidden", Span::call_site()))]
148                         .into_iter()
149                         .collect(),
150                 )),
151             ],
152         )),
153         TokenTree::Punct(Punct::new('#', Spacing::Alone)),
154         TokenTree::Group(Group::new(
155             Delimiter::Bracket,
156             tokens![
157                 TokenTree::Ident(Ident::new("allow", Span::call_site())),
158                 TokenTree::Group(Group::new(
159                     Delimiter::Parenthesis,
160                     tokens![TokenTree::Ident(Ident::new(
161                         "non_upper_case_globals",
162                         Span::call_site(),
163                     ))]
164                 )),
165             ],
166         )),
167         TokenTree::Punct(Punct::new('#', Spacing::Alone)),
168         TokenTree::Group(Group::new(
169             Delimiter::Bracket,
170             tokens![
171                 TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
172                 TokenTree::Group(Group::new(
173                     Delimiter::Parenthesis,
174                     tokens![
175                         TokenTree::Ident(Ident::new("any", Span::call_site())),
176                         TokenTree::Group(Group::new(
177                             Delimiter::Parenthesis,
178                             tokens![
179                                 TokenTree::Ident(Ident::new("target_os", Span::call_site())),
180                                 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
181                                 TokenTree::Literal(Literal::string("linux")),
182                                 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
183                                 TokenTree::Ident(Ident::new("target_os", Span::call_site())),
184                                 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
185                                 TokenTree::Literal(Literal::string("freebsd")),
186                                 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
187                                 TokenTree::Ident(Ident::new("target_os", Span::call_site())),
188                                 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
189                                 TokenTree::Literal(Literal::string("netbsd")),
190                                 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
191                                 TokenTree::Ident(Ident::new("target_os", Span::call_site())),
192                                 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
193                                 TokenTree::Literal(Literal::string("android")),
194                             ],
195                         )),
196                         TokenTree::Punct(Punct::new(',', Spacing::Alone)),
197                         TokenTree::Ident(Ident::new("link_section", Span::call_site())),
198                         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
199                         TokenTree::Literal(Literal::string(".init_array")),
200                     ],
201                 ))
202             ],
203         )),
204         TokenTree::Punct(Punct::new('#', Spacing::Alone)),
205         TokenTree::Group(Group::new(
206             Delimiter::Bracket,
207             tokens![
208                 TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
209                 TokenTree::Group(Group::new(
210                     Delimiter::Parenthesis,
211                     tokens![
212                         TokenTree::Ident(Ident::new("target_os", Span::call_site())),
213                         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
214                         TokenTree::Literal(Literal::string("macos")),
215                         TokenTree::Punct(Punct::new(',', Spacing::Alone)),
216                         TokenTree::Ident(Ident::new("link_section", Span::call_site())),
217                         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
218                         TokenTree::Literal(Literal::string("__DATA_CONST,__mod_init_func")),
219                     ],
220                 ))
221             ],
222         )),
223         TokenTree::Punct(Punct::new('#', Spacing::Alone)),
224         TokenTree::Group(Group::new(
225             Delimiter::Bracket,
226             tokens![
227                 TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
228                 TokenTree::Group(Group::new(
229                     Delimiter::Parenthesis,
230                     tokens![
231                         TokenTree::Ident(Ident::new("target_os", Span::call_site())),
232                         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
233                         TokenTree::Literal(Literal::string("windows")),
234                         TokenTree::Punct(Punct::new(',', Spacing::Alone)),
235                         TokenTree::Ident(Ident::new("link_section", Span::call_site())),
236                         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
237                         TokenTree::Literal(Literal::string(".CRT$XCU")),
238                     ],
239                 ))
240             ],
241         )),
242         TokenTree::Ident(Ident::new("static", Span::call_site())),
243         ctor_ident.clone(),
244         TokenTree::Punct(Punct::new(':', Spacing::Alone)),
245         TokenTree::Ident(Ident::new("unsafe", Span::call_site())),
246         TokenTree::Ident(Ident::new("extern", Span::call_site())),
247         TokenTree::Literal(Literal::string("C")),
248         TokenTree::Ident(Ident::new("fn", Span::call_site())),
249         TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::default())),
250         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
251         TokenTree::Group(Group::new(
252             Delimiter::Brace,
253             tokens![
254                 TokenTree::Punct(Punct::new('#', Spacing::Alone)),
255                 TokenTree::Group(Group::new(
256                     Delimiter::Bracket,
257                     tokens![
258                         TokenTree::Ident(Ident::new("cfg_attr", Span::call_site())),
259                         TokenTree::Group(Group::new(
260                             Delimiter::Parenthesis,
261                             tokens![
262                                 TokenTree::Ident(Ident::new("any", Span::call_site())),
263                                 TokenTree::Group(Group::new(
264                                     Delimiter::Parenthesis,
265                                     tokens![
266                                         TokenTree::Ident(Ident::new(
267                                             "target_os",
268                                             Span::call_site()
269                                         )),
270                                         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
271                                         TokenTree::Literal(Literal::string("linux")),
272                                         TokenTree::Punct(Punct::new(',', Spacing::Alone)),
273                                         TokenTree::Ident(Ident::new(
274                                             "target_os",
275                                             Span::call_site()
276                                         )),
277                                         TokenTree::Punct(Punct::new('=', Spacing::Alone)),
278                                         TokenTree::Literal(Literal::string("android")),
279                                     ],
280                                 )),
281                                 TokenTree::Punct(Punct::new(',', Spacing::Alone)),
282                                 TokenTree::Ident(Ident::new("link_section", Span::call_site())),
283                                 TokenTree::Punct(Punct::new('=', Spacing::Alone)),
284                                 TokenTree::Literal(Literal::string(".text.startup")),
285                             ],
286                         ))
287                     ],
288                 )),
289                 TokenTree::Ident(Ident::new("unsafe", Span::call_site())),
290                 TokenTree::Ident(Ident::new("extern", Span::call_site())),
291                 TokenTree::Literal(Literal::string("C")),
292                 TokenTree::Ident(Ident::new("fn", Span::call_site())),
293                 ctor_ident.clone(),
294                 TokenTree::Group(Group::new(Delimiter::Parenthesis, TokenStream::default())),
295                 TokenTree::Group(Group::new(
296                     Delimiter::Brace,
297                     vec![
298                         TokenTree::Ident(Ident::new(&name, Span::call_site())),
299                         TokenTree::Group(Group::new(
300                             Delimiter::Parenthesis,
301                             TokenStream::default(),
302                         )),
303                     ]
304                     .into_iter()
305                     .collect(),
306                 )),
307                 ctor_ident.clone(),
308             ],
309         )),
310         TokenTree::Punct(Punct::new(';', Spacing::Alone)),
311     ]
312     .into_iter()
313     .chain(input.into_iter())
314     .collect()
315 }
316