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