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}