mlua/
scope.rs

1use std::any::Any;
2use std::cell::{Cell, RefCell};
3use std::marker::PhantomData;
4use std::mem;
5use std::os::raw::c_int;
6
7#[cfg(feature = "serialize")]
8use serde::Serialize;
9
10use crate::error::{Error, Result};
11use crate::function::Function;
12use crate::lua::Lua;
13use crate::types::{Callback, CallbackUpvalue, LuaRef, MaybeSend, SubtypeId};
14use crate::userdata::{
15    AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods,
16};
17use crate::userdata_impl::UserDataRegistry;
18use crate::util::{
19    self, assert_stack, check_stack, init_userdata_metatable, push_string, push_table,
20    rawset_field, short_type_name, take_userdata, StackGuard,
21};
22use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
23
24#[cfg(feature = "lua54")]
25use crate::userdata::USER_VALUE_MAXSLOT;
26
27#[cfg(feature = "async")]
28use std::future::Future;
29
30/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
31/// callbacks that are not required to be Send or 'static.
32///
33/// See [`Lua::scope`] for more details.
34///
35/// [`Lua::scope`]: crate::Lua.html::scope
36pub struct Scope<'lua, 'scope>
37where
38    'lua: 'scope,
39{
40    lua: &'lua Lua,
41    destructors: RefCell<Vec<(LuaRef<'lua>, DestructorCallback<'lua>)>>,
42    _scope_invariant: PhantomData<Cell<&'scope ()>>,
43}
44
45type DestructorCallback<'lua> = Box<dyn Fn(LuaRef<'lua>) -> Vec<Box<dyn Any>> + 'lua>;
46
47impl<'lua, 'scope> Scope<'lua, 'scope> {
48    pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> {
49        Scope {
50            lua,
51            destructors: RefCell::new(Vec::new()),
52            _scope_invariant: PhantomData,
53        }
54    }
55
56    /// Wraps a Rust function or closure, creating a callable Lua function handle to it.
57    ///
58    /// This is a version of [`Lua::create_function`] that creates a callback which expires on
59    /// scope drop. See [`Lua::scope`] for more details.
60    ///
61    /// [`Lua::create_function`]: crate::Lua::create_function
62    /// [`Lua::scope`]: crate::Lua::scope
63    pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result<Function<'lua>>
64    where
65        A: FromLuaMulti<'callback>,
66        R: IntoLuaMulti<'callback>,
67        F: Fn(&'callback Lua, A) -> Result<R> + 'scope,
68    {
69        // Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
70        // callback itself must be 'scope lifetime, so the function should not be able to capture
71        // anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and
72        // the 'callback lifetime here can't be enlarged due to coming from a universal
73        // quantification in Lua::scope.
74        //
75        // I hope I got this explanation right, but in any case this is tested with compiletest_rs
76        // to make sure callbacks can't capture handles with lifetime outside the scope, inside the
77        // scope, and owned inside the callback itself.
78        unsafe {
79            self.create_callback(Box::new(move |lua, nargs| {
80                let args = A::from_stack_args(nargs, 1, None, lua)?;
81                func(lua, args)?.push_into_stack_multi(lua)
82            }))
83        }
84    }
85
86    /// Wraps a Rust mutable closure, creating a callable Lua function handle to it.
87    ///
88    /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires
89    /// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details.
90    ///
91    /// [`Lua::create_function_mut`]: crate::Lua::create_function_mut
92    /// [`Lua::scope`]: crate::Lua::scope
93    /// [`Scope::create_function`]: #method.create_function
94    pub fn create_function_mut<'callback, A, R, F>(
95        &'callback self,
96        func: F,
97    ) -> Result<Function<'lua>>
98    where
99        A: FromLuaMulti<'callback>,
100        R: IntoLuaMulti<'callback>,
101        F: FnMut(&'callback Lua, A) -> Result<R> + 'scope,
102    {
103        let func = RefCell::new(func);
104        self.create_function(move |lua, args| {
105            (*func
106                .try_borrow_mut()
107                .map_err(|_| Error::RecursiveMutCallback)?)(lua, args)
108        })
109    }
110
111    /// Creates a Lua userdata object from a custom userdata type.
112    ///
113    /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
114    /// scope drop, and does not require that the userdata type be Send (but still requires that the
115    /// UserData be 'static).
116    /// See [`Lua::scope`] for more details.
117    ///
118    /// [`Lua::create_userdata`]: crate::Lua::create_userdata
119    /// [`Lua::scope`]: crate::Lua::scope
120    pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
121    where
122        T: UserData + 'static,
123    {
124        // Safe even though T may not be Send, because the parent Lua cannot be sent to another
125        // thread while the Scope is alive (or the returned AnyUserData handle even).
126        unsafe {
127            let ud = self.lua.make_userdata(UserDataCell::new(data))?;
128            self.seal_userdata::<T>(&ud)?;
129            Ok(ud)
130        }
131    }
132
133    /// Creates a Lua userdata object from a custom serializable userdata type.
134    ///
135    /// This is a version of [`Lua::create_ser_userdata`] that creates a userdata which expires on
136    /// scope drop, and does not require that the userdata type be Send (but still requires that the
137    /// UserData be 'static).
138    /// See [`Lua::scope`] for more details.
139    ///
140    /// Requires `feature = "serialize"`
141    ///
142    /// [`Lua::create_ser_userdata`]: crate::Lua::create_ser_userdata
143    /// [`Lua::scope`]: crate::Lua::scope
144    #[cfg(feature = "serialize")]
145    #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
146    pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
147    where
148        T: UserData + Serialize + 'static,
149    {
150        unsafe {
151            let ud = self.lua.make_userdata(UserDataCell::new_ser(data))?;
152            self.seal_userdata::<T>(&ud)?;
153            Ok(ud)
154        }
155    }
156
157    /// Creates a Lua userdata object from a reference to custom userdata type.
158    ///
159    /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
160    /// scope drop, and does not require that the userdata type be Send. This method takes non-'static
161    /// reference to the data. See [`Lua::scope`] for more details.
162    ///
163    /// Userdata created with this method will not be able to be mutated from Lua.
164    pub fn create_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
165    where
166        T: UserData + 'static,
167    {
168        unsafe {
169            let ud = self.lua.make_userdata(UserDataCell::new_ref(data))?;
170            self.seal_userdata::<T>(&ud)?;
171            Ok(ud)
172        }
173    }
174
175    /// Creates a Lua userdata object from a mutable reference to custom userdata type.
176    ///
177    /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
178    /// scope drop, and does not require that the userdata type be Send. This method takes non-'static
179    /// mutable reference to the data. See [`Lua::scope`] for more details.
180    pub fn create_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
181    where
182        T: UserData + 'static,
183    {
184        unsafe {
185            let ud = self.lua.make_userdata(UserDataCell::new_ref_mut(data))?;
186            self.seal_userdata::<T>(&ud)?;
187            Ok(ud)
188        }
189    }
190
191    /// Creates a Lua userdata object from a custom Rust type.
192    ///
193    /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
194    /// scope drop and does not require that the userdata type be Send (but still requires that the
195    /// UserData be 'static). See [`Lua::scope`] for more details.
196    #[inline]
197    pub fn create_any_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
198    where
199        T: 'static,
200    {
201        unsafe {
202            let ud = self.lua.make_any_userdata(UserDataCell::new(data))?;
203            self.seal_userdata::<T>(&ud)?;
204            Ok(ud)
205        }
206    }
207
208    /// Creates a Lua userdata object from a reference to custom Rust type.
209    ///
210    /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
211    /// scope drop, and does not require that the Rust type be Send. This method takes non-'static
212    /// reference to the data. See [`Lua::scope`] for more details.
213    ///
214    /// Userdata created with this method will not be able to be mutated from Lua.
215    pub fn create_any_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
216    where
217        T: 'static,
218    {
219        unsafe {
220            let ud = self.lua.make_any_userdata(UserDataCell::new_ref(data))?;
221            self.seal_userdata::<T>(&ud)?;
222            Ok(ud)
223        }
224    }
225
226    /// Creates a Lua userdata object from a mutable reference to custom Rust type.
227    ///
228    /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
229    /// scope drop, and does not require that the Rust type be Send. This method takes non-'static
230    /// mutable reference to the data. See [`Lua::scope`] for more details.
231    pub fn create_any_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
232    where
233        T: 'static,
234    {
235        let lua = self.lua;
236        unsafe {
237            let ud = lua.make_any_userdata(UserDataCell::new_ref_mut(data))?;
238            self.seal_userdata::<T>(&ud)?;
239            Ok(ud)
240        }
241    }
242
243    /// Shortens the lifetime of a userdata to the lifetime of the scope.
244    unsafe fn seal_userdata<T: 'static>(&self, ud: &AnyUserData<'lua>) -> Result<()> {
245        #[cfg(any(feature = "lua51", feature = "luajit"))]
246        let newtable = self.lua.create_table()?;
247        let destructor: DestructorCallback = Box::new(move |ud| {
248            let state = ud.lua.state();
249            let _sg = StackGuard::new(state);
250            assert_stack(state, 2);
251
252            // Check that userdata is not destructed (via `take()` call)
253            if ud.lua.push_userdata_ref(&ud).is_err() {
254                return vec![];
255            }
256
257            // Clear associated user values
258            #[cfg(feature = "lua54")]
259            for i in 1..=USER_VALUE_MAXSLOT {
260                ffi::lua_pushnil(state);
261                ffi::lua_setiuservalue(state, -2, i as _);
262            }
263            #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
264            {
265                ffi::lua_pushnil(state);
266                ffi::lua_setuservalue(state, -2);
267            }
268            #[cfg(any(feature = "lua51", feature = "luajit"))]
269            {
270                ud.lua.push_ref(&newtable.0);
271                ffi::lua_setuservalue(state, -2);
272            }
273
274            vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
275        });
276        self.destructors
277            .borrow_mut()
278            .push((ud.0.clone(), destructor));
279
280        Ok(())
281    }
282
283    /// Creates a Lua userdata object from a custom userdata type.
284    ///
285    /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
286    /// scope drop, and does not require that the userdata type be Send or 'static. See
287    /// [`Lua::scope`] for more details.
288    ///
289    /// Lifting the requirement that the UserData type be 'static comes with some important
290    /// limitations, so if you only need to eliminate the Send requirement, it is probably better to
291    /// use [`Scope::create_userdata`] instead.
292    ///
293    /// The main limitation that comes from using non-'static userdata is that the produced userdata
294    /// will no longer have a `TypeId` associated with it, because `TypeId` can only work for
295    /// 'static types. This means that it is impossible, once the userdata is created, to get a
296    /// reference to it back *out* of an `AnyUserData` handle. This also implies that the
297    /// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept
298    /// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use
299    /// a single metatable for multiple non-'static types, so there is a higher cost associated with
300    /// creating the userdata metatable each time a new userdata is created.
301    ///
302    /// [`Scope::create_userdata`]: #method.create_userdata
303    /// [`Lua::create_userdata`]: crate::Lua::create_userdata
304    /// [`Lua::scope`]:crate::Lua::scope
305    /// [`UserDataMethods`]: crate::UserDataMethods
306    pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
307    where
308        T: UserData + 'scope,
309    {
310        // 'callback outliving 'scope is a lie to make the types work out, required due to the
311        // inability to work with the more correct callback type that is universally quantified over
312        // 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
313        // lifetime, so none of the static methods UserData types can add can possibly capture
314        // parameters.
315        unsafe fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
316            scope: &Scope<'lua, 'scope>,
317            ud_ptr: *const UserDataCell<T>,
318            name: &str,
319            method: NonStaticMethod<'callback, T>,
320        ) -> Result<Function<'lua>> {
321            // On methods that actually receive the userdata, we fake a type check on the passed in
322            // userdata, where we pretend there is a unique type per call to
323            // `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call
324            // it on a mismatched userdata type, which when using normal 'static userdata will fail
325            // with a type mismatch, but here without this check would proceed as though you had
326            // called the method on the original value (since we otherwise completely ignore the
327            // first argument).
328            let func_name = format!("{}.{name}", short_type_name::<T>());
329            let check_self_type = move |lua: &Lua, nargs: c_int| -> Result<&UserDataCell<T>> {
330                let state = lua.state();
331                if nargs > 0 && ffi::lua_touserdata(state, -nargs) as *const _ == ud_ptr {
332                    return Ok(&*ud_ptr);
333                }
334                Err(Error::bad_self_argument(
335                    &func_name,
336                    Error::UserDataTypeMismatch,
337                ))
338            };
339
340            match method {
341                NonStaticMethod::Method(method) => {
342                    let f = Box::new(move |lua, nargs| {
343                        let data = check_self_type(lua, nargs)?;
344                        let data = data.try_borrow()?;
345                        method(lua, &*data, nargs - 1)
346                    });
347                    scope.create_callback(f)
348                }
349                NonStaticMethod::MethodMut(method) => {
350                    let method = RefCell::new(method);
351                    let f = Box::new(move |lua, nargs| {
352                        let data = check_self_type(lua, nargs)?;
353                        let mut method = method
354                            .try_borrow_mut()
355                            .map_err(|_| Error::RecursiveMutCallback)?;
356                        let mut data = data.try_borrow_mut()?;
357                        (*method)(lua, &mut *data, nargs - 1)
358                    });
359                    scope.create_callback(f)
360                }
361                NonStaticMethod::Function(function) => scope.create_callback(function),
362                NonStaticMethod::FunctionMut(function) => {
363                    let function = RefCell::new(function);
364                    let f = Box::new(move |lua, nargs| {
365                        let mut func = function
366                            .try_borrow_mut()
367                            .map_err(|_| Error::RecursiveMutCallback)?;
368                        func(lua, nargs)
369                    });
370                    scope.create_callback(f)
371                }
372            }
373        }
374
375        let mut registry = NonStaticUserDataRegistry::new();
376        T::add_fields(&mut registry);
377        T::add_methods(&mut registry);
378
379        let lua = self.lua;
380        let state = lua.state();
381        unsafe {
382            let _sg = StackGuard::new(state);
383            check_stack(state, 13)?;
384
385            #[cfg(not(feature = "luau"))]
386            let ud_ptr = protect_lua!(state, 0, 1, |state| {
387                let ud = ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<T>>());
388
389                // Set empty environment for Lua 5.1
390                #[cfg(any(feature = "lua51", feature = "luajit"))]
391                {
392                    ffi::lua_newtable(state);
393                    ffi::lua_setuservalue(state, -2);
394                }
395
396                ud as *const UserDataCell<T>
397            })?;
398            #[cfg(feature = "luau")]
399            let ud_ptr = {
400                util::push_userdata(state, UserDataCell::new(data), true)?;
401                ffi::lua_touserdata(state, -1) as *const UserDataCell<T>
402            };
403
404            // Prepare metatable, add meta methods first and then meta fields
405            let meta_methods_nrec = registry.meta_methods.len() + registry.meta_fields.len() + 1;
406            push_table(state, 0, meta_methods_nrec, true)?;
407            for (k, m) in registry.meta_methods {
408                lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
409                rawset_field(state, -2, MetaMethod::validate(&k)?)?;
410            }
411            let mut has_name = false;
412            for (k, f) in registry.meta_fields {
413                has_name = has_name || k == MetaMethod::Type;
414                mlua_assert!(f(lua, 0)? == 1, "field function must return one value");
415                rawset_field(state, -2, MetaMethod::validate(&k)?)?;
416            }
417            // Set `__name/__type` if not provided
418            if !has_name {
419                let type_name = short_type_name::<T>();
420                push_string(state, type_name.as_bytes(), !lua.unlikely_memory_error())?;
421                rawset_field(state, -2, MetaMethod::Type.name())?;
422            }
423            let metatable_index = ffi::lua_absindex(state, -1);
424
425            let fields_nrec = registry.fields.len();
426            if fields_nrec > 0 {
427                // If __index is a table then update it inplace
428                let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index"));
429                match index_type {
430                    ffi::LUA_TNIL | ffi::LUA_TTABLE => {
431                        if index_type == ffi::LUA_TNIL {
432                            // Create a new table
433                            ffi::lua_pop(state, 1);
434                            push_table(state, 0, fields_nrec, true)?;
435                        }
436                        for (k, f) in registry.fields {
437                            #[rustfmt::skip]
438                            let NonStaticMethod::Function(f) = f else { unreachable!() };
439                            mlua_assert!(f(lua, 0)? == 1, "field function must return one value");
440                            rawset_field(state, -2, &k)?;
441                        }
442                        rawset_field(state, metatable_index, "__index")?;
443                    }
444                    _ => {
445                        // Propagate fields to the field getters
446                        for (k, f) in registry.fields {
447                            registry.field_getters.push((k, f))
448                        }
449                    }
450                }
451            }
452
453            let mut field_getters_index = None;
454            let field_getters_nrec = registry.field_getters.len();
455            if field_getters_nrec > 0 {
456                push_table(state, 0, field_getters_nrec, true)?;
457                for (k, m) in registry.field_getters {
458                    lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
459                    rawset_field(state, -2, &k)?;
460                }
461                field_getters_index = Some(ffi::lua_absindex(state, -1));
462            }
463
464            let mut field_setters_index = None;
465            let field_setters_nrec = registry.field_setters.len();
466            if field_setters_nrec > 0 {
467                push_table(state, 0, field_setters_nrec, true)?;
468                for (k, m) in registry.field_setters {
469                    lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
470                    rawset_field(state, -2, &k)?;
471                }
472                field_setters_index = Some(ffi::lua_absindex(state, -1));
473            }
474
475            let mut methods_index = None;
476            let methods_nrec = registry.methods.len();
477            if methods_nrec > 0 {
478                // Create table used for methods lookup
479                push_table(state, 0, methods_nrec, true)?;
480                for (k, m) in registry.methods {
481                    lua.push(wrap_method(self, ud_ptr, &k, m)?)?;
482                    rawset_field(state, -2, &k)?;
483                }
484                methods_index = Some(ffi::lua_absindex(state, -1));
485            }
486
487            #[cfg(feature = "luau")]
488            let extra_init = None;
489            #[cfg(not(feature = "luau"))]
490            let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
491                ffi::lua_pushcfunction(state, util::userdata_destructor::<UserDataCell<T>>);
492                rawset_field(state, -2, "__gc")
493            });
494
495            init_userdata_metatable(
496                state,
497                metatable_index,
498                field_getters_index,
499                field_setters_index,
500                methods_index,
501                extra_init,
502            )?;
503
504            let count = field_getters_index.map(|_| 1).unwrap_or(0)
505                + field_setters_index.map(|_| 1).unwrap_or(0)
506                + methods_index.map(|_| 1).unwrap_or(0);
507            ffi::lua_pop(state, count);
508
509            let mt_ptr = ffi::lua_topointer(state, -1);
510            // Write userdata just before attaching metatable with `__gc` metamethod
511            #[cfg(not(feature = "luau"))]
512            std::ptr::write(ud_ptr as _, UserDataCell::new(data));
513            ffi::lua_setmetatable(state, -2);
514            let ud = AnyUserData(lua.pop_ref(), SubtypeId::None);
515            lua.register_raw_userdata_metatable(mt_ptr, None);
516
517            #[cfg(any(feature = "lua51", feature = "luajit"))]
518            let newtable = lua.create_table()?;
519            let destructor: DestructorCallback = Box::new(move |ud| {
520                let state = ud.lua.state();
521                let _sg = StackGuard::new(state);
522                assert_stack(state, 2);
523
524                // Check that userdata is valid (very likely)
525                if ud.lua.push_userdata_ref(&ud).is_err() {
526                    return vec![];
527                }
528
529                // Deregister metatable
530                ffi::lua_getmetatable(state, -1);
531                let mt_ptr = ffi::lua_topointer(state, -1);
532                ffi::lua_pop(state, 1);
533                ud.lua.deregister_raw_userdata_metatable(mt_ptr);
534
535                // Clear associated user values
536                #[cfg(feature = "lua54")]
537                for i in 1..=USER_VALUE_MAXSLOT {
538                    ffi::lua_pushnil(state);
539                    ffi::lua_setiuservalue(state, -2, i as _);
540                }
541                #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
542                {
543                    ffi::lua_pushnil(state);
544                    ffi::lua_setuservalue(state, -2);
545                }
546                #[cfg(any(feature = "lua51", feature = "luajit"))]
547                {
548                    ud.lua.push_ref(&newtable.0);
549                    ffi::lua_setuservalue(state, -2);
550                }
551
552                // A hack to drop non-static `T`
553                unsafe fn seal<T>(t: T) -> Box<dyn FnOnce() + 'static> {
554                    let f: Box<dyn FnOnce()> = Box::new(move || drop(t));
555                    mem::transmute(f)
556                }
557
558                let ud = take_userdata::<UserDataCell<T>>(state);
559                vec![Box::new(seal(ud))]
560            });
561            self.destructors
562                .borrow_mut()
563                .push((ud.0.clone(), destructor));
564
565            Ok(ud)
566        }
567    }
568
569    // Unsafe, because the callback can improperly capture any value with 'callback scope, such as
570    // improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the
571    // lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick
572    // a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback
573    // must NOT capture any parameters.
574    unsafe fn create_callback<'callback>(
575        &self,
576        f: Callback<'callback, 'scope>,
577    ) -> Result<Function<'lua>> {
578        let f = mem::transmute::<Callback<'callback, 'scope>, Callback<'lua, 'static>>(f);
579        let f = self.lua.create_callback(f)?;
580
581        let destructor: DestructorCallback = Box::new(|f| {
582            let state = f.lua.state();
583            let _sg = StackGuard::new(state);
584            assert_stack(state, 3);
585
586            f.lua.push_ref(&f);
587
588            // We know the destructor has not run yet because we hold a reference to the callback.
589
590            ffi::lua_getupvalue(state, -1, 1);
591            let ud = take_userdata::<CallbackUpvalue>(state);
592            ffi::lua_pushnil(state);
593            ffi::lua_setupvalue(state, -2, 1);
594
595            vec![Box::new(ud)]
596        });
597        self.destructors
598            .borrow_mut()
599            .push((f.0.clone(), destructor));
600
601        Ok(f)
602    }
603}
604
605impl<'lua, 'scope> Drop for Scope<'lua, 'scope> {
606    fn drop(&mut self) {
607        // We separate the action of invalidating the userdata in Lua and actually dropping the
608        // userdata type into two phases. This is so that, in the event a userdata drop panics, we
609        // can be sure that all of the userdata in Lua is actually invalidated.
610
611        // All destructors are non-panicking, so this is fine
612        let to_drop = self
613            .destructors
614            .get_mut()
615            .drain(..)
616            .flat_map(|(r, dest)| dest(r))
617            .collect::<Vec<_>>();
618
619        drop(to_drop);
620    }
621}
622
623#[allow(clippy::type_complexity)]
624enum NonStaticMethod<'lua, T> {
625    Method(Box<dyn Fn(&'lua Lua, &T, c_int) -> Result<c_int>>),
626    MethodMut(Box<dyn FnMut(&'lua Lua, &mut T, c_int) -> Result<c_int>>),
627    Function(Box<dyn Fn(&'lua Lua, c_int) -> Result<c_int>>),
628    FunctionMut(Box<dyn FnMut(&'lua Lua, c_int) -> Result<c_int>>),
629}
630
631struct NonStaticUserDataRegistry<'lua, T> {
632    // Fields
633    fields: Vec<(String, NonStaticMethod<'lua, T>)>,
634    field_getters: Vec<(String, NonStaticMethod<'lua, T>)>,
635    field_setters: Vec<(String, NonStaticMethod<'lua, T>)>,
636    meta_fields: Vec<(String, Callback<'lua, 'static>)>,
637
638    // Methods
639    methods: Vec<(String, NonStaticMethod<'lua, T>)>,
640    meta_methods: Vec<(String, NonStaticMethod<'lua, T>)>,
641}
642
643impl<'lua, T> NonStaticUserDataRegistry<'lua, T> {
644    const fn new() -> NonStaticUserDataRegistry<'lua, T> {
645        NonStaticUserDataRegistry {
646            fields: Vec::new(),
647            field_getters: Vec::new(),
648            field_setters: Vec::new(),
649            meta_fields: Vec::new(),
650            methods: Vec::new(),
651            meta_methods: Vec::new(),
652        }
653    }
654}
655
656impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
657    fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
658    where
659        V: IntoLua<'lua> + Clone + 'static,
660    {
661        let name = name.as_ref().to_string();
662        self.fields.push((
663            name,
664            NonStaticMethod::Function(Box::new(move |lua, _| unsafe {
665                value.clone().push_into_stack_multi(lua)
666            })),
667        ));
668    }
669
670    fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
671    where
672        M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
673        R: IntoLua<'lua>,
674    {
675        let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| unsafe {
676            method(lua, ud)?.push_into_stack_multi(lua)
677        }));
678        self.field_getters.push((name.as_ref().into(), method));
679    }
680
681    fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, mut method: M)
682    where
683        M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
684        A: FromLua<'lua>,
685    {
686        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
687        let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
688            let val = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
689            method(lua, ud, val)?.push_into_stack_multi(lua)
690        }));
691        self.field_setters.push((name.as_ref().into(), method));
692    }
693
694    fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
695    where
696        F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
697        R: IntoLua<'lua>,
698    {
699        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
700        let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
701            let ud = AnyUserData::from_stack_args(nargs, 1, Some(&func_name), lua)?;
702            function(lua, ud)?.push_into_stack_multi(lua)
703        }));
704        self.field_getters.push((name.as_ref().into(), func));
705    }
706
707    fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, mut function: F)
708    where
709        F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
710        A: FromLua<'lua>,
711    {
712        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
713        let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
714            let (ud, val) = <_>::from_stack_args(nargs, 1, Some(&func_name), lua)?;
715            function(lua, ud, val)?.push_into_stack_multi(lua)
716        }));
717        self.field_setters.push((name.as_ref().into(), func));
718    }
719
720    fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
721    where
722        V: IntoLua<'lua> + Clone + 'static,
723    {
724        let name = name.as_ref().to_string();
725        let name2 = name.clone();
726        self.meta_fields.push((
727            name,
728            Box::new(move |lua, _| unsafe {
729                UserDataRegistry::<()>::check_meta_field(lua, &name2, value.clone())?
730                    .push_into_stack_multi(lua)
731            }),
732        ));
733    }
734
735    fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
736    where
737        F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
738        R: IntoLua<'lua>,
739    {
740        let name = name.as_ref().to_string();
741        let name2 = name.clone();
742        self.meta_fields.push((
743            name,
744            Box::new(move |lua, _| unsafe {
745                UserDataRegistry::<()>::check_meta_field(lua, &name2, f(lua)?)?
746                    .push_into_stack_multi(lua)
747            }),
748        ));
749    }
750}
751
752impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> {
753    fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
754    where
755        M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
756        A: FromLuaMulti<'lua>,
757        R: IntoLuaMulti<'lua>,
758    {
759        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
760        let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
761            let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
762            method(lua, ud, args)?.push_into_stack_multi(lua)
763        }));
764        self.methods.push((name.as_ref().into(), method));
765    }
766
767    fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, mut method: M)
768    where
769        M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
770        A: FromLuaMulti<'lua>,
771        R: IntoLuaMulti<'lua>,
772    {
773        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
774        let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
775            let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
776            method(lua, ud, args)?.push_into_stack_multi(lua)
777        }));
778        self.methods.push((name.as_ref().into(), method));
779    }
780
781    #[cfg(feature = "async")]
782    fn add_async_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
783    where
784        'lua: 's,
785        T: 'static,
786        M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
787        A: FromLuaMulti<'lua>,
788        MR: Future<Output = Result<R>> + 's,
789        R: IntoLuaMulti<'lua>,
790    {
791        // The panic should never happen as async non-static code wouldn't compile
792        // Non-static lifetime must be bounded to 'lua lifetime
793        panic!("asynchronous methods are not supported for non-static userdata")
794    }
795
796    #[cfg(feature = "async")]
797    fn add_async_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
798    where
799        'lua: 's,
800        T: 'static,
801        M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
802        A: FromLuaMulti<'lua>,
803        MR: Future<Output = Result<R>> + 's,
804        R: IntoLuaMulti<'lua>,
805    {
806        // The panic should never happen as async non-static code wouldn't compile
807        // Non-static lifetime must be bounded to 'lua lifetime
808        panic!("asynchronous methods are not supported for non-static userdata")
809    }
810
811    fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
812    where
813        F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
814        A: FromLuaMulti<'lua>,
815        R: IntoLuaMulti<'lua>,
816    {
817        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
818        let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
819            let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
820            function(lua, args)?.push_into_stack_multi(lua)
821        }));
822        self.methods.push((name.as_ref().into(), func));
823    }
824
825    fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, mut function: F)
826    where
827        F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
828        A: FromLuaMulti<'lua>,
829        R: IntoLuaMulti<'lua>,
830    {
831        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
832        let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
833            let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
834            function(lua, args)?.push_into_stack_multi(lua)
835        }));
836        self.methods.push((name.as_ref().into(), func));
837    }
838
839    #[cfg(feature = "async")]
840    fn add_async_function<F, A, FR, R>(&mut self, _name: impl AsRef<str>, _function: F)
841    where
842        F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
843        A: FromLuaMulti<'lua>,
844        FR: Future<Output = Result<R>> + 'lua,
845        R: IntoLuaMulti<'lua>,
846    {
847        // The panic should never happen as async non-static code wouldn't compile
848        // Non-static lifetime must be bounded to 'lua lifetime
849        panic!("asynchronous functions are not supported for non-static userdata")
850    }
851
852    fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
853    where
854        M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
855        A: FromLuaMulti<'lua>,
856        R: IntoLuaMulti<'lua>,
857    {
858        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
859        let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe {
860            let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
861            method(lua, ud, args)?.push_into_stack_multi(lua)
862        }));
863        self.meta_methods.push((name.as_ref().into(), method));
864    }
865
866    fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, mut method: M)
867    where
868        M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
869        A: FromLuaMulti<'lua>,
870        R: IntoLuaMulti<'lua>,
871    {
872        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
873        let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe {
874            let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?;
875            method(lua, ud, args)?.push_into_stack_multi(lua)
876        }));
877        self.meta_methods.push((name.as_ref().into(), method));
878    }
879
880    #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
881    fn add_async_meta_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
882    where
883        'lua: 's,
884        T: 'static,
885        M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
886        A: FromLuaMulti<'lua>,
887        MR: Future<Output = Result<R>> + 's,
888        R: IntoLuaMulti<'lua>,
889    {
890        // The panic should never happen as async non-static code wouldn't compile
891        // Non-static lifetime must be bounded to 'lua lifetime
892        panic!("asynchronous meta methods are not supported for non-static userdata")
893    }
894
895    #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
896    fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef<str>, _method: M)
897    where
898        'lua: 's,
899        T: 'static,
900        M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
901        A: FromLuaMulti<'lua>,
902        MR: Future<Output = Result<R>> + 's,
903        R: IntoLuaMulti<'lua>,
904    {
905        // The panic should never happen as async non-static code wouldn't compile
906        // Non-static lifetime must be bounded to 'lua lifetime
907        panic!("asynchronous meta methods are not supported for non-static userdata")
908    }
909
910    fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
911    where
912        F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
913        A: FromLuaMulti<'lua>,
914        R: IntoLuaMulti<'lua>,
915    {
916        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
917        let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe {
918            let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
919            function(lua, args)?.push_into_stack_multi(lua)
920        }));
921        self.meta_methods.push((name.as_ref().into(), func));
922    }
923
924    fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, mut function: F)
925    where
926        F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
927        A: FromLuaMulti<'lua>,
928        R: IntoLuaMulti<'lua>,
929    {
930        let func_name = format!("{}.{}", short_type_name::<T>(), name.as_ref());
931        let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe {
932            let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?;
933            function(lua, args)?.push_into_stack_multi(lua)
934        }));
935        self.meta_methods.push((name.as_ref().into(), func));
936    }
937
938    #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
939    fn add_async_meta_function<F, A, FR, R>(&mut self, _name: impl AsRef<str>, _function: F)
940    where
941        F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
942        A: FromLuaMulti<'lua>,
943        FR: Future<Output = Result<R>> + 'lua,
944        R: IntoLuaMulti<'lua>,
945    {
946        // The panic should never happen as async non-static code wouldn't compile
947        // Non-static lifetime must be bounded to 'lua lifetime
948        panic!("asynchronous meta functions are not supported for non-static userdata")
949    }
950}