1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/// Exports a given list of functions to a Erlang module.
///
/// This should be called exactly once in every NIF library. It will wrap and export the given rust
/// functions into the Erlang module.
///
/// The first argument is a string specifying what Erlang/Elixir module you want the function
/// exported into. In Erlang this will simply be the atom you named your module. In Elixir, all
/// modules are prefixed with `Elixir.<module path>`
///
/// The second argument is a list of 3-tuples. Each tuple contains information on a single exported
/// NIF function. The first tuple item is the name you want to export the function into, the second
/// is the arity (number of arguments) of the exported function. The third argument is a
/// identifier of a rust function. This is where your actual NIF will be implemented.
///
/// The third argument is an `Option<fn(env: &Env, load_info: Term) -> bool>`. If this is
/// `Some`, the function will execute when the NIF is first loaded by the BEAM.
#[macro_export]
#[deprecated(since = "0.22.0", note = "Please use `rustler::init!` instead.")]
macro_rules! rustler_export_nifs {
    // Strip trailing comma.
    ($name:expr, [$( $exported_nif:tt ),+,], $on_load:expr) => {
        $crate::rustler_export_nifs!($name, [$( $exported_nif ),*], $on_load);
    };
    ($name:expr, [$( $exported_nif:tt ),*], $on_load:expr) => {
        static mut NIF_ENTRY: Option<$crate::codegen_runtime::DEF_NIF_ENTRY> = None;

        $crate::rustler_export_nifs!(internal_platform_init, ({
            // TODO: If an unwrap ever happens, we will unwind right into C! Fix this!

            extern "C" fn nif_load(
                env: $crate::codegen_runtime::NIF_ENV,
                _priv_data: *mut *mut $crate::codegen_runtime::c_void,
                load_info: $crate::codegen_runtime::NIF_TERM)
                -> $crate::codegen_runtime::c_int {
                unsafe {
                    $crate::codegen_runtime::handle_nif_init_call($on_load, env, load_info)
                }
            }

            const FUN_ENTRIES: &'static [$crate::codegen_runtime::DEF_NIF_FUNC] = &[
                $($crate::rustler_export_nifs!(internal_item_init, $exported_nif)),*
            ];

            let entry = $crate::codegen_runtime::DEF_NIF_ENTRY {
                major: $crate::codegen_runtime::NIF_MAJOR_VERSION,
                minor: $crate::codegen_runtime::NIF_MINOR_VERSION,
                name: concat!($name, "\x00") as *const str as *const $crate::codegen_runtime::c_char,
                num_of_funcs: FUN_ENTRIES.len() as $crate::codegen_runtime::c_int,
                funcs: FUN_ENTRIES.as_ptr(),
                load: Some(nif_load),
                reload: None,
                upgrade: None,
                unload: None,
                vm_variant: b"beam.vanilla\x00".as_ptr() as *const $crate::codegen_runtime::c_char,
                options: 0,
                sizeof_ErlNifResourceTypeInit: $crate::codegen_runtime::get_nif_resource_type_init_size(),
            };
            unsafe { NIF_ENTRY = Some(entry) };

            unsafe { NIF_ENTRY.as_ref().unwrap() }
        }));
    };

    (internal_item_init, ($nif_name:expr, $nif_arity:expr, $nif_fun:path)) => {
        $crate::rustler_export_nifs!(internal_item_init, ($nif_name, $nif_arity, $nif_fun, $crate::schedule::SchedulerFlags::Normal))
    };
    (internal_item_init, ($nif_name:expr, $nif_arity:expr, $nif_fun:path, $nif_flag:expr)) => {
        $crate::codegen_runtime::DEF_NIF_FUNC {
            name: concat!($nif_name, "\x00") as *const str as *const $crate::codegen_runtime::c_char,
            arity: $nif_arity,
            function: {
                extern "C" fn nif_func(
                    env: $crate::codegen_runtime::NIF_ENV,
                    argc: $crate::codegen_runtime::c_int,
                    argv: *const $crate::codegen_runtime::NIF_TERM)
                    -> $crate::codegen_runtime::NIF_TERM {
                        unsafe {
                            $crate::rustler_export_nifs!(
                                internal_handle_nif_call, ($nif_fun, $nif_arity, env, argc, argv))
                        }
                }
                nif_func
            },
            flags: ($nif_flag as $crate::schedule::SchedulerFlags) as u32,
        }
    };

    (internal_handle_nif_call, ($fun:path, $arity:expr, $env:expr, $argc:expr, $argv:expr)) => ({
        use $crate::Term;
        let env_lifetime = ();
        let env = $crate::Env::new(&env_lifetime, $env);

        let terms = ::std::slice::from_raw_parts($argv, $argc as usize)
            .iter()
            .map(|x| $crate::Term::new(env, *x))
            .collect::<Vec<Term>>();

        let result: ::std::thread::Result<_> = ::std::panic::catch_unwind(move || {
            $crate::codegen_runtime::NifReturnable::into_returned($fun(env, &terms), env)
        });

        match result {
            Ok(res) => res.apply(env),
            Err(_err) => $crate::codegen_runtime::raise_exception(
                env.as_c_arg(),
                $crate::types::atom::Atom::from_bytes(env, b"nif_panic").ok().unwrap().as_c_arg(),
            ),
        }
    });

    (internal_platform_init, ($inner:expr)) => {
        #[cfg(not(feature = "alternative_nif_init_name"))]
        #[cfg(unix)]
        #[no_mangle]
        extern "C" fn nif_init() -> *const $crate::codegen_runtime::DEF_NIF_ENTRY {
            $inner
        }

        #[cfg(not(feature = "alternative_nif_init_name"))]
        #[cfg(windows)]
        #[no_mangle]
        extern "C" fn nif_init(callbacks: *mut $crate::codegen_runtime::TWinDynNifCallbacks) -> *const $crate::codegen_runtime::DEF_NIF_ENTRY {
            unsafe {
                $crate::codegen_runtime::WIN_DYN_NIF_CALLBACKS = Some(*callbacks);
            }

            $inner
        }

        #[cfg(feature = "alternative_nif_init_name")]
        #[cfg(unix)]
        #[no_mangle]
        extern "C" fn rustler_nif_init() -> *const $crate::codegen_runtime::DEF_NIF_ENTRY {
            $inner
        }

        #[cfg(feature = "alternative_nif_init_name")]
        #[cfg(windows)]
        #[no_mangle]
        extern "C" fn rustler_nif_init(callbacks: *mut $crate::codegen_runtime::TWinDynNifCallbacks) -> *const $crate::codegen_runtime::DEF_NIF_ENTRY {
            unsafe {
                $crate::codegen_runtime::WIN_DYN_NIF_CALLBACKS = Some(*callbacks);
            }

            $inner
        }
    };
}