chromium/third_party/rust/chromium_crates_io/vendor/small_ctor-0.1.2/src/lib.rs

//! A dependency free library that declares functions to be automatically
//! executed before `main` is invoked.
//!
//! This crate implements the exact same behavior as the
//! [`ctor`](https://crates.io/crates/ctor) crate with the following
//! differences:
//!
//! * It has no dependencies other than `proc_macro` itself.
//! * It requires that functions are marked with `unsafe`.
//! * It can only be used with functions, not static items.
//! * It only supports `#[ctor]`
//!
//! ## Example
//!
//! This is a motivating example that registers a struct as a plugin in a
//! hypothetical global plugins registry:
//!
//! ```
//! struct MyPlugin;
//! # impl MyPlugin { fn register(&self, _: MyPlugin) {} }
//! # static PLUGINS: MyPlugin = MyPlugin;
//!
//! #[small_ctor::ctor]
//! unsafe fn register_plugin() {
//!     PLUGINS.register(MyPlugin);
//! }
//! ```
//!
//! ## Safety
//!
//! This library involves "life before main" which is explicitly not permitted
//! in Rust which is why this library is anything but safe.  In fact, it's a
//! really bad idea to do what this crate is promoting.  For instance at
//! present code that runs in `#[ctor]` will run before `lang_start` managed to
//! execute.  Some of the effects of this are that the main thread does not have
//! a name yet, the stack protection is not enabled and code must not panic.
//!
//! It's recommended that the only thing you do in a `#[ctor]` function is to
//! append to a vector, insert into a hashmap or similar.
//!
//! ## Recommended Usage
//!
//! It's recommended to perform basic operations which are unlikely to panic
//! and to defer most work to when the actual `main` happens.  So for
//! instead instead of initializing plugins in the `ctor`, just push them
//! to a plugin registry and then trigger callbacks registered that way
//! regularly in `main`.
//!
//! ## Compiler and Linker Bugs
//!
//! Currently this library is prone to break due to compiler bugs in subtle
//! ways.  The core issue is [rust #47384](https://github.com/rust-lang/rust/issues/47384).
//! You can reduze the likelihood of it happening by disabling incremental
//! compilation or setting `codegen-units` to `1` in your profile in the
//! `Cargo.toml`.
//!
//! ## Destructors
//!
//! This crate intentionally does not support an at-exit mechanism.  The reason
//! for this is that those are running so late that even more Rust code is
//! unable to properly run.  Not only does panicking not work, the entire standard
//! IO system is already unusable.  More importantly on many platforms these
//! do not run properly.  For instance on macOS destructors do not run when
//! thread local storage is in use.  If you do want to use something like this
//! you can do something like invoke `libc::atexit` from within a `#[ctor]`
//! function.
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>()
    }
}

/// Marks a function or static variable as a library/executable constructor.
/// This uses OS-specific linker sections to call a specific function at load
/// time.
///
/// Multiple startup functions/statics are supported, but the invocation order
/// is not guaranteed.  For information about what is safe or not safe to do
/// in such functions refer to the module documention.
///
/// # Example
///
/// ```
/// # struct MyPlugin;
/// # impl MyPlugin { fn insert(&self, _: MyPlugin) {} }
/// #[small_ctor::ctor]
/// unsafe fn register_plugin() {
/// # let PLUGINS = MyPlugin;
///     PLUGINS.insert(MyPlugin);
/// }
/// ```
#[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()
}