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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
use std::ops::Deref;

use once_cell::sync::OnceCell;

use emacs_module::emacs_value;

use super::*;

/// A "global reference" that can live outside the scope of an [`Env`]. This is useful for sharing
/// an otherwise short-lived Lisp [`Value`] across multiple invocations of Rust functions defined
/// with [`defun`]. Examples include efficient access to interned symbols or Lisp functions, and
/// Rust-based multi-threading.
///
/// # Implementation
///
/// Cloning this struct requires an [`Env`], so it doesn't implement [`Clone`].
///
/// [`free_global_ref`] requires an [`Env`]. Therefore, to avoid leaking the underlying [`Value`],
/// [`free`] should be used to free a global reference, instead of [`drop`]. For the use case of
/// accessing interned symbols and Lisp functions, this is a non-issue, as the values are
/// supposed to be "static" anyway.
///
/// The above is a shortcoming in the design of emacs-module. There are 2 possible ways to fix it:
/// - Make [`free_global_ref`] work without an env, like Erlang's `enif_release_resource`.
/// - Allow `user_ptr`'s finalizer to access the env, to properly free associated global refs.
///
/// [`Env`]: struct.Env.html
/// [`Value`]: struct.Value.html
/// [`defun`]: attr.defun.html
/// [`Clone`]: https://doc.rust-lang.org/std/clone/trait.Clone.html
/// [`free_global_ref`]: https://www.gnu.org/software/emacs/manual/html_node/elisp/Module-Values.html
/// [`free`]: #method.free
/// [`drop`]: https://doc.rust-lang.org/std/mem/fn.drop.html
#[derive(Debug)]
#[repr(transparent)]
pub struct GlobalRef {
    raw: emacs_value
}

impl GlobalRef {
    /// Creates a new global reference for the given [`Value`].
    ///
    /// [`Value`]: struct.Value.html
    pub fn new(value: Value) -> Self {
        let env = value.env;
        // TODO: Check whether this really is `no_exit`.
        let raw = unsafe_raw_call_no_exit!(env, make_global_ref, value.raw);
        // NOTE: raw != value.raw
        Self { raw }
    }

    // For testing.
    pub(crate) unsafe fn from_raw(raw: emacs_value) -> Self {
        Self { raw }
    }

    /// Frees this global reference.
    pub fn free(self, env: &Env) -> Result<()> {
        // Safety: We assume user code doesn't directly call C function `free_global_ref`.
        unsafe_raw_call!(env, free_global_ref, self.raw)?;
        Ok(())
    }

    /// Returns the underlying [`Value`], scoping its lifetime to the given [`Env`].
    ///
    /// [`Env`]: struct.Env.html
    /// [`Value`]: struct.Value.html
    #[inline]
    pub fn bind<'e, 'g: 'e>(&'g self, env: &'e Env) -> Value<'e> {
        // Safety: This global ref keeps the underlying Lisp object alive.
        unsafe { Value::new(self.raw, env) }
    }

    /// Returns a copy of this global reference.
    pub fn clone(&self, env: &Env) -> Self {
        self.bind(env).make_global_ref()
    }
}

// Safety: Doing anything useful with a GlobalRef requires an &Env, which means holding the GIL.
unsafe impl Send for GlobalRef {}
unsafe impl Sync for GlobalRef {}

impl<'e> FromLisp<'e> for GlobalRef {
    #[inline(always)]
    fn from_lisp(value: Value<'e>) -> Result<Self> {
        Ok(Self::new(value))
    }
}

impl<'e> IntoLisp<'e> for &'e GlobalRef {
    #[inline(always)]
    fn into_lisp(self, env: &'e Env) -> Result<Value<'e>> {
        Ok(self.bind(env))
    }
}


impl<'e> Value<'e> {
    /// Creates a new [`GlobalRef`] for this value.
    ///
    /// [`GlobalRef`]: struct.GlobalRef.html
    #[inline(always)]
    pub fn make_global_ref(self) -> GlobalRef {
        GlobalRef::new(self)
    }
}

/// Declares global references. These will be initialized when the module is loaded.
#[doc(hidden)]
#[macro_export]
macro_rules! global_refs {
    ($($name:ident)*) => {
        $(
            #[allow(non_upper_case_globals)]
            pub static $name: &'static $crate::OnceGlobalRef = {
                static X: $crate::OnceGlobalRef = $crate::OnceGlobalRef::new();
                &X
            };
        )*
    };
    ($registrator_name:ident ($init_method:ident) =>
        $(
            $name:ident $( => $lisp_name:expr )?
        )*
    ) => {
        $crate::global_refs! {
            $($name)*
        }

        #[$crate::deps::ctor::ctor]
        fn $registrator_name() {
            $crate::init::__GLOBAL_REFS__.try_lock()
                .expect("Failed to acquire a write lock on the list of initializers for global refs")
                .push(::std::boxed::Box::new(|env| {
                    $(
                        #[allow(unused_variables)]
                        let name = $crate::deps::emacs_macros::lisp_name!($name);
                        $( let name = $lisp_name; )?
                        $crate::OnceGlobalRef::$init_method(&$name, env, name)?;
                    )*
                    Ok(())
                }));
        }
    };
}

/// A [`GlobalRef`] that can be initialized once. This is useful for long-lived values that should
/// be initialized when the dynamic module is loaded. A typical use case is specifying
/// frequently-used symbols, which can be done with the help of the macro [`use_symbols!`].
///
/// [`use_symbols`]: crate::use_symbols
#[derive(Debug)]
#[repr(transparent)]
pub struct OnceGlobalRef {
    inner: OnceCell<GlobalRef>
}

impl OnceGlobalRef {
    pub const fn new() -> Self {
        Self { inner: OnceCell::new() }
    }

    /// Initializes this global reference with the given function.
    #[doc(hidden)]
    pub fn init<F: FnOnce(&Env) -> Result<Value>>(&self, env: &Env, f: F) -> Result<&GlobalRef> {
        let g = f(env)?.make_global_ref();
        self.inner.set(g).expect("Cannot initialize a global reference more than once");
        Ok(self.inner.get().expect("Failed to get an initialized OnceGlobalRef"))
    }

    /// Points this global reference to an interned Lisp symbol with the given name.
    ///
    /// This should be called once, during module initialization.
    #[doc(hidden)]
    pub fn init_to_symbol(&self, env: &Env, name: &str) -> Result<&GlobalRef> {
        self.init(env, |env| env.intern(name))
    }

    /// Points this global reference to the function bound to the Lisp symbol with the given name.
    ///
    /// This should be called once, during module initialization.
    ///
    /// If the symbol is later bound to another function, this global reference will still point to
    /// the old function. Therefore, this is best used for built-in and primitive functions.
    #[doc(hidden)]
    pub fn init_to_function(&self, env: &Env, name: &str) -> Result<&GlobalRef> {
        self.init(env, |env| {
            let symbol = env.intern(name)?;
            env.call("indirect-function", [symbol])
        })
    }
}

impl<'e> IntoLisp<'e> for &'e OnceGlobalRef {
    #[inline(always)]
    fn into_lisp(self, env: &'e Env) -> Result<Value<'e>> {
        Ok(self.bind(env))
    }
}

impl Deref for OnceGlobalRef {
    type Target = GlobalRef;

    #[inline]
    fn deref(&self) -> &Self::Target {
        self.inner.get().expect("Cannot access an uninitialized global reference")
    }
}