Skip to main content

luaur_rt/
registry.rs

1//! [`RegistryKey`] — long-term storage of a Lua value in the registry.
2//!
3//! Mirrors `mlua::RegistryKey`. A registry key holds a value reachable by the
4//! GC for as long as the key (or a clone of it) is alive. It is created with
5//! [`Lua::create_registry_value`] and read back with [`Lua::registry_value`].
6//! Dropping the key (or calling [`Lua::remove_registry_value`]) releases the
7//! registry slot.
8//!
9//! Under the hood a `RegistryKey` is just a public wrapper around the same
10//! `lua_ref`/`lua_unref` machinery the internal handles already use
11//! ([`crate::state::LuaRef`]): `create_registry_value` pushes the value and
12//! takes a registry ref; `registry_value` re-pushes it. Each key remembers
13//! which [`Lua`] minted it so a key used with the wrong instance is rejected
14//! with [`Error::MismatchedRegistryKey`].
15
16use crate::error::{Error, Result};
17use crate::state::{Lua, LuaRef};
18use crate::sync::{NotSync, XRc, NOT_SYNC};
19use crate::traits::{FromLua, IntoLua};
20use crate::value::Value;
21
22/// An owned reference to a value stored in the Lua registry.
23///
24/// Mirrors `mlua::RegistryKey`. Cloning produces another handle to the **same**
25/// stored value (the slot is shared via `Rc`). The value stays alive until the
26/// last clone is dropped or it is explicitly removed.
27///
28/// Under the `send` feature it is `Send` but never `Sync` — see
29/// [`crate::sync::NotSync`].
30#[derive(Clone)]
31pub struct RegistryKey {
32    pub(crate) reference: XRc<LuaRef>,
33    pub(crate) _not_sync: NotSync,
34}
35
36impl RegistryKey {
37    pub(crate) fn from_ref(reference: LuaRef) -> RegistryKey {
38        RegistryKey {
39            reference: XRc::new(reference),
40            _not_sync: NOT_SYNC,
41        }
42    }
43
44    /// Push the stored value onto the owning state's stack.
45    pub(crate) fn push(&self) {
46        self.reference.push();
47    }
48}
49
50impl std::fmt::Debug for RegistryKey {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        // Include the registry slot id so two keys referring to different slots
53        // print differently (mlua's `RegistryKey` Debug exposes the slot too).
54        write!(f, "RegistryKey({})", self.reference.id())
55    }
56}
57
58// `RegistryKey` is usable as a hash-map key (mlua's `test_lua_registry_hash`).
59// Identity is the (state, registry-slot) pair: a clone shares the same slot, so
60// it hashes/compares equal; keys for distinct values use distinct slots.
61impl PartialEq for RegistryKey {
62    fn eq(&self, other: &Self) -> bool {
63        self.reference.state() == other.reference.state()
64            && self.reference.id() == other.reference.id()
65    }
66}
67
68impl Eq for RegistryKey {}
69
70impl std::hash::Hash for RegistryKey {
71    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
72        (self.reference.state() as usize).hash(state);
73        self.reference.id().hash(state);
74    }
75}
76
77impl Lua {
78    /// Store a value in the registry and return a [`RegistryKey`] that keeps it
79    /// alive. Mirrors `mlua::Lua::create_registry_value`.
80    pub fn create_registry_value(&self, value: impl IntoLua) -> Result<RegistryKey> {
81        let v = value.into_lua(self)?;
82        self.push_value(&v)?;
83        Ok(RegistryKey::from_ref(self.pop_ref()))
84    }
85
86    /// Read back a value previously stored with [`Lua::create_registry_value`],
87    /// converting it to `T`. Mirrors `mlua::Lua::registry_value`.
88    pub fn registry_value<T: FromLua>(&self, key: &RegistryKey) -> Result<T> {
89        if !self.owns_registry_value(key) {
90            return Err(Error::MismatchedRegistryKey);
91        }
92        let state = self.state();
93        let value = unsafe {
94            key.push();
95            let v = self.value_from_stack(-1)?;
96            crate::sys::lua_pop(state, 1);
97            v
98        };
99        T::from_lua(value, self)
100    }
101
102    /// Remove a value from the registry, releasing its slot. Mirrors
103    /// `mlua::Lua::remove_registry_value`.
104    pub fn remove_registry_value(&self, key: RegistryKey) -> Result<()> {
105        if !self.owns_registry_value(&key) {
106            return Err(Error::MismatchedRegistryKey);
107        }
108        // Dropping the key releases the underlying `lua_ref` slot.
109        drop(key);
110        Ok(())
111    }
112
113    /// Replace the value stored under an existing key. Mirrors
114    /// `mlua::Lua::replace_registry_value`.
115    pub fn replace_registry_value(&self, key: &mut RegistryKey, value: impl IntoLua) -> Result<()> {
116        if !self.owns_registry_value(key) {
117            return Err(Error::MismatchedRegistryKey);
118        }
119        *key = self.create_registry_value(value)?;
120        Ok(())
121    }
122
123    /// Whether this `Lua` instance owns `key` (i.e. `key` was minted by this VM,
124    /// not a different one). Mirrors `mlua::Lua::owns_registry_value`.
125    pub fn owns_registry_value(&self, key: &RegistryKey) -> bool {
126        // Two `Lua` handles share the same VM iff their inner state pointers are
127        // equal (cloning a `Lua` shares the `Rc<LuaInner>`; a separate
128        // `Lua::new()` has a distinct state).
129        key.reference.state() == self.state()
130    }
131
132    /// Expire any [`RegistryKey`]s whose strong handles have all been dropped.
133    ///
134    /// Mirrors `mlua::Lua::expire_registry_values`. luaur-rt releases a
135    /// registry slot eagerly when the last clone of its `RegistryKey` is dropped
136    /// (via [`crate::state::LuaRef`]'s `Drop` calling `lua_unref`), so there is
137    /// no deferred-expiry queue to drain; this is a no-op kept for parity.
138    pub fn expire_registry_values(&self) {}
139
140    /// Store a value in the registry under the string `name`. Mirrors
141    /// `mlua::Lua::set_named_registry_value`.
142    pub fn set_named_registry_value(&self, name: &str, value: impl IntoLua) -> Result<()> {
143        let v = value.into_lua(self)?;
144        let state = self.state();
145        let cname = std::ffi::CString::new(name)
146            .map_err(|_| Error::runtime("registry name contains a NUL byte"))?;
147        unsafe {
148            self.push_value(&v)?;
149            // lua_setfield pops the value and stores registry[name] = value.
150            crate::sys::lua_setfield(state, crate::sys::LUA_REGISTRYINDEX, cname.as_ptr());
151        }
152        Ok(())
153    }
154
155    /// Read back a value previously stored with
156    /// [`Lua::set_named_registry_value`], converting it to `T`. A name that was
157    /// never set (or was unset) reads back as `nil`. Mirrors
158    /// `mlua::Lua::named_registry_value`.
159    pub fn named_registry_value<T: FromLua>(&self, name: &str) -> Result<T> {
160        let state = self.state();
161        let cname = std::ffi::CString::new(name)
162            .map_err(|_| Error::runtime("registry name contains a NUL byte"))?;
163        let value = unsafe {
164            crate::sys::lua_getfield(state, crate::sys::LUA_REGISTRYINDEX, cname.as_ptr());
165            let v = self.value_from_stack(-1)?;
166            crate::sys::lua_pop(state, 1);
167            v
168        };
169        T::from_lua(value, self)
170    }
171
172    /// Remove a value stored under the string `name`. Mirrors
173    /// `mlua::Lua::unset_named_registry_value`.
174    pub fn unset_named_registry_value(&self, name: &str) -> Result<()> {
175        self.set_named_registry_value(name, Value::Nil)
176    }
177}
178
179// ---------------------------------------------------------------------------
180// Conversions: a RegistryKey behaves like the value it stores when packed, and
181// `Chunk::eval::<RegistryKey>()` stores the chunk's result.
182// ---------------------------------------------------------------------------
183
184impl IntoLua for RegistryKey {
185    fn into_lua(self, lua: &Lua) -> Result<Value> {
186        (&self).into_lua(lua)
187    }
188}
189
190impl IntoLua for &RegistryKey {
191    fn into_lua(self, lua: &Lua) -> Result<Value> {
192        if self.reference.state() != lua.state() {
193            return Err(Error::MismatchedRegistryKey);
194        }
195        let state = lua.state();
196        unsafe {
197            self.push();
198            let v = lua.value_from_stack(-1)?;
199            crate::sys::lua_pop(state, 1);
200            Ok(v)
201        }
202    }
203}
204
205impl FromLua for RegistryKey {
206    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
207        lua.create_registry_value(value)
208    }
209}