Skip to main content

mlua/
scope.rs

1use std::cell::RefCell;
2use std::marker::PhantomData;
3use std::mem;
4
5use crate::error::{Error, Result};
6use crate::function::Function;
7use crate::state::{Lua, LuaGuard, RawLua};
8use crate::traits::{FromLuaMulti, IntoLuaMulti};
9use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
10use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
11use crate::util::{self, StackGuard, check_stack, get_metatable_ptr, get_userdata, take_userdata};
12
13/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
14/// callbacks that are not required to be `Send` or `'static`.
15///
16/// See [`Lua::scope`] for more details.
17pub struct Scope<'scope, 'env: 'scope> {
18    // Invalidate scoped values before releasing the Lua lock, then run user destructors.
19    destructors: Destructors<'env>,
20    lua: LuaGuard,
21    user_destructors: UserDestructors<'env>,
22    _scope_invariant: PhantomData<&'scope mut &'scope ()>,
23    _env_invariant: PhantomData<&'env mut &'env ()>,
24}
25
26type DestructorCallback<'a> = Box<dyn FnOnce(&RawLua, ValueRef) -> Vec<Box<dyn FnOnce() + 'a>>>;
27
28// Implement Drop on Destructors instead of Scope to avoid compilation error
29struct Destructors<'a>(RefCell<Vec<(ValueRef, DestructorCallback<'a>)>>);
30
31struct UserDestructors<'a>(RefCell<Vec<Box<dyn FnOnce() + 'a>>>);
32
33impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
34    pub(crate) fn new(lua: LuaGuard) -> Self {
35        Scope {
36            destructors: Destructors(RefCell::new(Vec::new())),
37            lua,
38            user_destructors: UserDestructors(RefCell::new(Vec::new())),
39            _scope_invariant: PhantomData,
40            _env_invariant: PhantomData,
41        }
42    }
43
44    /// Wraps a Rust function or closure, creating a callable Lua function handle to it.
45    ///
46    /// This is a version of [`Lua::create_function`] that creates a callback which expires on
47    /// scope drop. See [`Lua::scope`] for more details.
48    pub fn create_function<F, A, R>(&'scope self, func: F) -> Result<Function>
49    where
50        F: Fn(&Lua, A) -> Result<R> + 'scope,
51        A: FromLuaMulti,
52        R: IntoLuaMulti,
53    {
54        unsafe {
55            self.create_callback(Box::new(move |rawlua, nargs| {
56                let args = A::from_stack_args(nargs, 1, None, rawlua)?;
57                func(rawlua.lua(), args)?.push_into_stack_multi(rawlua)
58            }))
59        }
60    }
61
62    /// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
63    ///
64    /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
65    /// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
66    pub fn create_function_mut<F, A, R>(&'scope self, func: F) -> Result<Function>
67    where
68        F: FnMut(&Lua, A) -> Result<R> + 'scope,
69        A: FromLuaMulti,
70        R: IntoLuaMulti,
71    {
72        let func = RefCell::new(func);
73        self.create_function(move |lua, args| {
74            (*func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
75        })
76    }
77
78    /// Creates a Lua userdata object from a reference to custom userdata type.
79    ///
80    /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
81    /// scope drop, and does not require that the userdata type be Send. This method takes
82    /// non-'static reference to the data. See [`Lua::scope`] for more details.
83    ///
84    /// Userdata created with this method will not be able to be mutated from Lua.
85    pub fn create_userdata_ref<T>(&'scope self, data: &'env T) -> Result<AnyUserData>
86    where
87        T: UserData + 'static,
88    {
89        let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref(data)) }?;
90        self.seal_userdata::<T>(&ud);
91        Ok(ud)
92    }
93
94    /// Creates a Lua userdata object from a mutable reference to custom userdata type.
95    ///
96    /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
97    /// scope drop, and does not require that the userdata type be Send. This method takes
98    /// non-'static mutable reference to the data. See [`Lua::scope`] for more details.
99    pub fn create_userdata_ref_mut<T>(&'scope self, data: &'env mut T) -> Result<AnyUserData>
100    where
101        T: UserData + 'static,
102    {
103        let ud = unsafe { self.lua.make_userdata(UserDataStorage::new_ref_mut(data)) }?;
104        self.seal_userdata::<T>(&ud);
105        Ok(ud)
106    }
107
108    /// Creates a Lua userdata object from a reference to custom Rust type.
109    ///
110    /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
111    /// scope drop, and does not require that the Rust type be Send. This method takes non-'static
112    /// reference to the data. See [`Lua::scope`] for more details.
113    ///
114    /// Userdata created with this method will not be able to be mutated from Lua.
115    pub fn create_any_userdata_ref<T>(&'scope self, data: &'env T) -> Result<AnyUserData>
116    where
117        T: 'static,
118    {
119        let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref(data)) }?;
120        self.seal_userdata::<T>(&ud);
121        Ok(ud)
122    }
123
124    /// Creates a Lua userdata object from a mutable reference to custom Rust type.
125    ///
126    /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
127    /// scope drop, and does not require that the Rust type be Send. This method takes non-'static
128    /// mutable reference to the data. See [`Lua::scope`] for more details.
129    pub fn create_any_userdata_ref_mut<T>(&'scope self, data: &'env mut T) -> Result<AnyUserData>
130    where
131        T: 'static,
132    {
133        let ud = unsafe { self.lua.make_any_userdata(UserDataStorage::new_ref_mut(data)) }?;
134        self.seal_userdata::<T>(&ud);
135        Ok(ud)
136    }
137
138    /// Creates a Lua userdata object from a custom userdata type.
139    ///
140    /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
141    /// scope drop, and does not require that the userdata type be `Send` or `'static`. See
142    /// [`Lua::scope`] for more details.
143    ///
144    /// The main limitation that comes from using non-'static userdata is that the produced userdata
145    /// will no longer have a [`TypeId`] associated with it, because [`TypeId`] can only work for
146    /// `'static` types. This means that it is impossible, once the userdata is created, to get a
147    /// reference to it back *out* of an [`AnyUserData`] handle. This also implies that the
148    /// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
149    /// [`AnyUserData`] as a first parameter) are vastly less useful. Also, there is no way to
150    /// re-use a single metatable for multiple non-'static types, so there is a higher cost
151    /// associated with creating the userdata metatable each time a new userdata is created.
152    ///
153    /// [`TypeId`]: std::any::TypeId
154    /// [`UserDataMethods`]: crate::UserDataMethods
155    pub fn create_userdata<T>(&'scope self, data: T) -> Result<AnyUserData>
156    where
157        T: UserData + 'env,
158    {
159        self.create_any_userdata(data, T::register)
160    }
161
162    /// Creates a Lua userdata object from a custom Rust type.
163    ///
164    /// Since the Rust type is not required to be static and implement [`UserData`] trait,
165    /// you need to provide a function to register fields or methods for the object.
166    ///
167    /// See also [`Scope::create_userdata`] for more details about non-static limitations.
168    pub fn create_any_userdata<T>(
169        &'scope self,
170        data: T,
171        register: impl FnOnce(&mut UserDataRegistry<T>),
172    ) -> Result<AnyUserData>
173    where
174        T: 'env,
175    {
176        let state = self.lua.state();
177        let ud = unsafe {
178            let _sg = StackGuard::new(state);
179            check_stack(state, 3)?;
180
181            // Delay initialization until the metatable is ready
182            let protect = !self.lua.unlikely_memory_error();
183            let ud_ptr = util::push_uninit_userdata::<UserDataStorage<T>>(state, protect)?;
184
185            // Push the metatable and register it with no TypeId
186            let mut registry = UserDataRegistry::new_unique(self.lua.lua(), ud_ptr as *mut _);
187            register(&mut registry);
188            self.lua.push_userdata_metatable(registry.into_raw())?;
189            let mt_ptr = ffi::lua_topointer(state, -1);
190            self.lua.register_userdata_metatable(mt_ptr, None);
191
192            // Write data to the pointer and attach metatable
193            std::ptr::write(ud_ptr, UserDataStorage::new_scoped(data));
194            ffi::lua_setmetatable(state, -2);
195
196            // Keep the userdata on the stack so it can be invalidated on failure.
197            ffi::lua_xpush(state, self.lua.ref_thread(), -1);
198            match self.lua.try_pop_ref_thread() {
199                Ok(vref) => AnyUserData(vref),
200                Err(err) => {
201                    self.lua.deregister_userdata_metatable(mt_ptr);
202                    drop(take_userdata::<UserDataStorage<T>>(state, -1));
203                    return Err(err);
204                }
205            }
206        };
207        self.seal_userdata::<T>(&ud);
208        Ok(ud)
209    }
210
211    /// Adds a destructor function to be run when the scope ends.
212    ///
213    /// This functionality is useful for cleaning up any resources after the scope ends.
214    ///
215    /// # Example
216    ///
217    /// ```rust
218    /// # use mlua::{Error, Lua, Result};
219    /// # fn main() -> Result<()> {
220    /// let lua = Lua::new();
221    /// let ud = lua.create_any_userdata(String::from("hello"))?;
222    /// lua.scope(|scope| {
223    ///     scope.add_destructor(|| {
224    ///         _ = ud.take::<String>();
225    ///     });
226    ///     // Run the code that uses `ud` here
227    ///    Ok(())
228    /// })?;
229    /// assert!(matches!(ud.borrow::<String>(), Err(Error::UserDataDestructed)));
230    /// # Ok(())
231    /// # }
232    pub fn add_destructor(&'scope self, destructor: impl FnOnce() + 'env) {
233        self.user_destructors.0.borrow_mut().push(Box::new(destructor));
234    }
235
236    unsafe fn create_callback(&'scope self, f: ScopedCallback<'scope>) -> Result<Function> {
237        let f = mem::transmute::<ScopedCallback, Callback>(f);
238        let f = self.lua.create_callback(f)?;
239
240        let destructor: DestructorCallback = Box::new(|rawlua, vref| {
241            let ref_thread = rawlua.ref_thread();
242            ffi::lua_getupvalue(ref_thread, vref.index, 1);
243            let upvalue = get_userdata::<CallbackUpvalue>(ref_thread, -1);
244            let data = (*upvalue).data.take();
245            ffi::lua_pop(ref_thread, 1);
246            vec![Box::new(move || drop(data))]
247        });
248        self.destructors.0.borrow_mut().push((f.0.clone(), destructor));
249
250        Ok(f)
251    }
252
253    /// Shortens the lifetime of the userdata to the lifetime of the scope.
254    fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) {
255        let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe {
256            // Ensure that userdata is not destructed
257            match rawlua.get_userdata_ref_type_id(&vref) {
258                Ok(Some(_)) => {}
259                Ok(None) => {
260                    // Deregister metatable
261                    let mt_ptr = get_metatable_ptr(rawlua.ref_thread(), vref.index);
262                    rawlua.deregister_userdata_metatable(mt_ptr);
263                }
264                Err(_) => return vec![],
265            }
266
267            let data = take_userdata::<UserDataStorage<T>>(rawlua.ref_thread(), vref.index);
268            vec![Box::new(move || drop(data))]
269        });
270        self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));
271    }
272}
273
274impl Drop for Destructors<'_> {
275    fn drop(&mut self) {
276        // We separate the action of invalidating the userdata in Lua and actually dropping the
277        // userdata type into two phases. This is so that, in the event a userdata drop panics,
278        // we can be sure that all of the userdata in Lua is actually invalidated.
279
280        let destructors = mem::take(&mut *self.0.borrow_mut());
281        if let Some(lua) = destructors.first().map(|(vref, _)| vref.lua.lock()) {
282            // All destructors are non-panicking, so this is fine
283            let to_drop = destructors
284                .into_iter()
285                .flat_map(|(vref, destructor)| destructor(&lua, vref))
286                .collect::<Vec<_>>();
287
288            drop(to_drop);
289        }
290    }
291}
292
293impl Drop for UserDestructors<'_> {
294    fn drop(&mut self) {
295        let destructors = mem::take(&mut *self.0.borrow_mut());
296        for destructor in destructors {
297            destructor();
298        }
299    }
300}