mlua/
userdata.rs

1use std::any::TypeId;
2use std::ffi::CStr;
3use std::fmt;
4use std::hash::Hash;
5use std::os::raw::{c_char, c_void};
6use std::string::String as StdString;
7
8use crate::error::{Error, Result};
9use crate::function::Function;
10use crate::state::Lua;
11use crate::string::String;
12use crate::table::{Table, TablePairs};
13use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
14use crate::types::{MaybeSend, ValueRef};
15use crate::util::{check_stack, get_userdata, push_string, short_type_name, take_userdata, StackGuard};
16use crate::value::Value;
17
18#[cfg(feature = "async")]
19use std::future::Future;
20
21#[cfg(feature = "serde")]
22use {
23    serde::ser::{self, Serialize, Serializer},
24    std::result::Result as StdResult,
25};
26
27// Re-export for convenience
28pub(crate) use cell::UserDataStorage;
29pub use r#ref::{UserDataRef, UserDataRefMut};
30pub use registry::UserDataRegistry;
31pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
32pub(crate) use util::{
33    borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata, init_userdata_metatable,
34    TypeIdHints,
35};
36
37/// Kinds of metamethods that can be overridden.
38///
39/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
40/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
41#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42#[non_exhaustive]
43pub enum MetaMethod {
44    /// The `+` operator.
45    Add,
46    /// The `-` operator.
47    Sub,
48    /// The `*` operator.
49    Mul,
50    /// The `/` operator.
51    Div,
52    /// The `%` operator.
53    Mod,
54    /// The `^` operator.
55    Pow,
56    /// The unary minus (`-`) operator.
57    Unm,
58    /// The floor division (//) operator.
59    #[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
60    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))))]
61    IDiv,
62    /// The bitwise AND (&) operator.
63    #[cfg(any(feature = "lua54", feature = "lua53"))]
64    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
65    BAnd,
66    /// The bitwise OR (|) operator.
67    #[cfg(any(feature = "lua54", feature = "lua53"))]
68    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
69    BOr,
70    /// The bitwise XOR (binary ~) operator.
71    #[cfg(any(feature = "lua54", feature = "lua53"))]
72    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
73    BXor,
74    /// The bitwise NOT (unary ~) operator.
75    #[cfg(any(feature = "lua54", feature = "lua53"))]
76    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
77    BNot,
78    /// The bitwise left shift (<<) operator.
79    #[cfg(any(feature = "lua54", feature = "lua53"))]
80    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
81    Shl,
82    /// The bitwise right shift (>>) operator.
83    #[cfg(any(feature = "lua54", feature = "lua53"))]
84    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
85    Shr,
86    /// The string concatenation operator `..`.
87    Concat,
88    /// The length operator `#`.
89    Len,
90    /// The `==` operator.
91    Eq,
92    /// The `<` operator.
93    Lt,
94    /// The `<=` operator.
95    Le,
96    /// Index access `obj[key]`.
97    Index,
98    /// Index write access `obj[key] = value`.
99    NewIndex,
100    /// The call "operator" `obj(arg1, args2, ...)`.
101    Call,
102    /// The `__tostring` metamethod.
103    ///
104    /// This is not an operator, but will be called by methods such as `tostring` and `print`.
105    ToString,
106    /// The `__pairs` metamethod.
107    ///
108    /// This is not an operator, but it will be called by the built-in `pairs` function.
109    #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
110    #[cfg_attr(
111        docsrs,
112        doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52")))
113    )]
114    Pairs,
115    /// The `__ipairs` metamethod.
116    ///
117    /// This is not an operator, but it will be called by the built-in [`ipairs`] function.
118    ///
119    /// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
120    #[cfg(any(feature = "lua52", feature = "luajit52", doc))]
121    #[cfg_attr(docsrs, doc(cfg(any(feature = "lua52", feature = "luajit52"))))]
122    IPairs,
123    /// The `__iter` metamethod.
124    ///
125    /// Executed before the iteration begins, and should return an iterator function like `next`
126    /// (or a custom one).
127    #[cfg(any(feature = "luau", doc))]
128    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
129    Iter,
130    /// The `__close` metamethod.
131    ///
132    /// Executed when a variable, that marked as to-be-closed, goes out of scope.
133    ///
134    /// More information about to-be-closed variables can be found in the Lua 5.4
135    /// [documentation][lua_doc].
136    ///
137    /// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
138    #[cfg(feature = "lua54")]
139    #[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
140    Close,
141    /// The `__name`/`__type` metafield.
142    ///
143    /// This is not a function, but it's value can be used by `tostring` and `typeof` built-in
144    /// functions.
145    #[doc(hidden)]
146    Type,
147}
148
149impl PartialEq<MetaMethod> for &str {
150    fn eq(&self, other: &MetaMethod) -> bool {
151        *self == other.name()
152    }
153}
154
155impl PartialEq<MetaMethod> for StdString {
156    fn eq(&self, other: &MetaMethod) -> bool {
157        self == other.name()
158    }
159}
160
161impl fmt::Display for MetaMethod {
162    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
163        write!(fmt, "{}", self.name())
164    }
165}
166
167impl MetaMethod {
168    /// Returns Lua metamethod name, usually prefixed by two underscores.
169    pub const fn name(self) -> &'static str {
170        match self {
171            MetaMethod::Add => "__add",
172            MetaMethod::Sub => "__sub",
173            MetaMethod::Mul => "__mul",
174            MetaMethod::Div => "__div",
175            MetaMethod::Mod => "__mod",
176            MetaMethod::Pow => "__pow",
177            MetaMethod::Unm => "__unm",
178
179            #[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
180            MetaMethod::IDiv => "__idiv",
181            #[cfg(any(feature = "lua54", feature = "lua53"))]
182            MetaMethod::BAnd => "__band",
183            #[cfg(any(feature = "lua54", feature = "lua53"))]
184            MetaMethod::BOr => "__bor",
185            #[cfg(any(feature = "lua54", feature = "lua53"))]
186            MetaMethod::BXor => "__bxor",
187            #[cfg(any(feature = "lua54", feature = "lua53"))]
188            MetaMethod::BNot => "__bnot",
189            #[cfg(any(feature = "lua54", feature = "lua53"))]
190            MetaMethod::Shl => "__shl",
191            #[cfg(any(feature = "lua54", feature = "lua53"))]
192            MetaMethod::Shr => "__shr",
193
194            MetaMethod::Concat => "__concat",
195            MetaMethod::Len => "__len",
196            MetaMethod::Eq => "__eq",
197            MetaMethod::Lt => "__lt",
198            MetaMethod::Le => "__le",
199            MetaMethod::Index => "__index",
200            MetaMethod::NewIndex => "__newindex",
201            MetaMethod::Call => "__call",
202            MetaMethod::ToString => "__tostring",
203
204            #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
205            MetaMethod::Pairs => "__pairs",
206            #[cfg(any(feature = "lua52", feature = "luajit52"))]
207            MetaMethod::IPairs => "__ipairs",
208            #[cfg(feature = "luau")]
209            MetaMethod::Iter => "__iter",
210
211            #[cfg(feature = "lua54")]
212            MetaMethod::Close => "__close",
213
214            #[rustfmt::skip]
215            MetaMethod::Type => if cfg!(feature = "luau") { "__type" } else { "__name" },
216        }
217    }
218
219    pub(crate) const fn as_cstr(self) -> &'static CStr {
220        match self {
221            #[rustfmt::skip]
222            MetaMethod::Type => if cfg!(feature = "luau") { c"__type" } else { c"__name" },
223            _ => unreachable!(),
224        }
225    }
226
227    pub(crate) fn validate(name: &str) -> Result<&str> {
228        match name {
229            "__gc" => Err(Error::MetaMethodRestricted(name.to_string())),
230            "__metatable" => Err(Error::MetaMethodRestricted(name.to_string())),
231            _ if name.starts_with("__mlua") => Err(Error::MetaMethodRestricted(name.to_string())),
232            name => Ok(name),
233        }
234    }
235}
236
237impl AsRef<str> for MetaMethod {
238    fn as_ref(&self) -> &str {
239        self.name()
240    }
241}
242
243impl From<MetaMethod> for StdString {
244    #[inline]
245    fn from(method: MetaMethod) -> Self {
246        method.name().to_owned()
247    }
248}
249
250/// Method registry for [`UserData`] implementors.
251pub trait UserDataMethods<T> {
252    /// Add a regular method which accepts a `&T` as the first parameter.
253    ///
254    /// Regular methods are implemented by overriding the `__index` metamethod and returning the
255    /// accessed method. This allows them to be used with the expected `userdata:method()` syntax.
256    ///
257    /// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
258    /// be used as a fall-back if no regular method is found.
259    fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
260    where
261        M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
262        A: FromLuaMulti,
263        R: IntoLuaMulti;
264
265    /// Add a regular method which accepts a `&mut T` as the first parameter.
266    ///
267    /// Refer to [`add_method`] for more information about the implementation.
268    ///
269    /// [`add_method`]: UserDataMethods::add_method
270    fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
271    where
272        M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
273        A: FromLuaMulti,
274        R: IntoLuaMulti;
275
276    /// Add a method which accepts `T` as the first parameter.
277    ///
278    /// The userdata `T` will be moved out of the userdata container. This is useful for
279    /// methods that need to consume the userdata.
280    ///
281    /// The method can be called only once per userdata instance, subsequent calls will result in a
282    /// [`Error::UserDataDestructed`] error.
283    #[doc(hidden)]
284    fn add_method_once<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
285    where
286        T: 'static,
287        M: Fn(&Lua, T, A) -> Result<R> + MaybeSend + 'static,
288        A: FromLuaMulti,
289        R: IntoLuaMulti,
290    {
291        let name = name.into();
292        let method_name = format!("{}.{name}", short_type_name::<T>());
293        self.add_function(name, move |lua, (ud, args): (AnyUserData, A)| {
294            let this = (ud.take()).map_err(|err| Error::bad_self_argument(&method_name, err))?;
295            method(lua, this, args)
296        });
297    }
298
299    /// Add an async method which accepts a `&T` as the first parameter and returns [`Future`].
300    ///
301    /// Refer to [`add_method`] for more information about the implementation.
302    ///
303    /// [`add_method`]: UserDataMethods::add_method
304    #[cfg(feature = "async")]
305    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
306    fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
307    where
308        T: 'static,
309        M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
310        A: FromLuaMulti,
311        MR: Future<Output = Result<R>> + MaybeSend + 'static,
312        R: IntoLuaMulti;
313
314    /// Add an async method which accepts a `&mut T` as the first parameter and returns [`Future`].
315    ///
316    /// Refer to [`add_method`] for more information about the implementation.
317    ///
318    /// [`add_method`]: UserDataMethods::add_method
319    #[cfg(feature = "async")]
320    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
321    fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
322    where
323        T: 'static,
324        M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
325        A: FromLuaMulti,
326        MR: Future<Output = Result<R>> + MaybeSend + 'static,
327        R: IntoLuaMulti;
328
329    /// Add an async method which accepts a `T` as the first parameter and returns [`Future`].
330    ///
331    /// The userdata `T` will be moved out of the userdata container. This is useful for
332    /// methods that need to consume the userdata.
333    ///
334    /// The method can be called only once per userdata instance, subsequent calls will result in a
335    /// [`Error::UserDataDestructed`] error.
336    #[cfg(feature = "async")]
337    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
338    #[doc(hidden)]
339    fn add_async_method_once<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
340    where
341        T: 'static,
342        M: Fn(Lua, T, A) -> MR + MaybeSend + 'static,
343        A: FromLuaMulti,
344        MR: Future<Output = Result<R>> + MaybeSend + 'static,
345        R: IntoLuaMulti,
346    {
347        let name = name.into();
348        let method_name = format!("{}.{name}", short_type_name::<T>());
349        self.add_async_function(name, move |lua, (ud, args): (AnyUserData, A)| {
350            match (ud.take()).map_err(|err| Error::bad_self_argument(&method_name, err)) {
351                Ok(this) => either::Either::Left(method(lua, this, args)),
352                Err(err) => either::Either::Right(async move { Err(err) }),
353            }
354        });
355    }
356
357    /// Add a regular method as a function which accepts generic arguments.
358    ///
359    /// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
360    /// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
361    /// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
362    fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
363    where
364        F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
365        A: FromLuaMulti,
366        R: IntoLuaMulti;
367
368    /// Add a regular method as a mutable function which accepts generic arguments.
369    ///
370    /// This is a version of [`add_function`] that accepts a `FnMut` argument.
371    ///
372    /// [`add_function`]: UserDataMethods::add_function
373    fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
374    where
375        F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
376        A: FromLuaMulti,
377        R: IntoLuaMulti;
378
379    /// Add a regular method as an async function which accepts generic arguments and returns
380    /// [`Future`].
381    ///
382    /// This is an async version of [`add_function`].
383    ///
384    /// [`add_function`]: UserDataMethods::add_function
385    #[cfg(feature = "async")]
386    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
387    fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
388    where
389        F: Fn(Lua, A) -> FR + MaybeSend + 'static,
390        A: FromLuaMulti,
391        FR: Future<Output = Result<R>> + MaybeSend + 'static,
392        R: IntoLuaMulti;
393
394    /// Add a metamethod which accepts a `&T` as the first parameter.
395    ///
396    /// # Note
397    ///
398    /// This can cause an error with certain binary metamethods that can trigger if only the right
399    /// side has a metatable. To prevent this, use [`add_meta_function`].
400    ///
401    /// [`add_meta_function`]: UserDataMethods::add_meta_function
402    fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
403    where
404        M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
405        A: FromLuaMulti,
406        R: IntoLuaMulti;
407
408    /// Add a metamethod as a function which accepts a `&mut T` as the first parameter.
409    ///
410    /// # Note
411    ///
412    /// This can cause an error with certain binary metamethods that can trigger if only the right
413    /// side has a metatable. To prevent this, use [`add_meta_function`].
414    ///
415    /// [`add_meta_function`]: UserDataMethods::add_meta_function
416    fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
417    where
418        M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
419        A: FromLuaMulti,
420        R: IntoLuaMulti;
421
422    /// Add an async metamethod which accepts a `&T` as the first parameter and returns [`Future`].
423    ///
424    /// This is an async version of [`add_meta_method`].
425    ///
426    /// [`add_meta_method`]: UserDataMethods::add_meta_method
427    #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
428    #[cfg_attr(
429        docsrs,
430        doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
431    )]
432    fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
433    where
434        T: 'static,
435        M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
436        A: FromLuaMulti,
437        MR: Future<Output = Result<R>> + MaybeSend + 'static,
438        R: IntoLuaMulti;
439
440    /// Add an async metamethod which accepts a `&mut T` as the first parameter and returns
441    /// [`Future`].
442    ///
443    /// This is an async version of [`add_meta_method_mut`].
444    ///
445    /// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
446    #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
447    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
448    fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
449    where
450        T: 'static,
451        M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
452        A: FromLuaMulti,
453        MR: Future<Output = Result<R>> + MaybeSend + 'static,
454        R: IntoLuaMulti;
455
456    /// Add a metamethod which accepts generic arguments.
457    ///
458    /// Metamethods for binary operators can be triggered if either the left or right argument to
459    /// the binary operator has a metatable, so the first argument here is not necessarily a
460    /// userdata of type `T`.
461    fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
462    where
463        F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
464        A: FromLuaMulti,
465        R: IntoLuaMulti;
466
467    /// Add a metamethod as a mutable function which accepts generic arguments.
468    ///
469    /// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
470    ///
471    /// [`add_meta_function`]: UserDataMethods::add_meta_function
472    fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
473    where
474        F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
475        A: FromLuaMulti,
476        R: IntoLuaMulti;
477
478    /// Add a metamethod which accepts generic arguments and returns [`Future`].
479    ///
480    /// This is an async version of [`add_meta_function`].
481    ///
482    /// [`add_meta_function`]: UserDataMethods::add_meta_function
483    #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
484    #[cfg_attr(
485        docsrs,
486        doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
487    )]
488    fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
489    where
490        F: Fn(Lua, A) -> FR + MaybeSend + 'static,
491        A: FromLuaMulti,
492        FR: Future<Output = Result<R>> + MaybeSend + 'static,
493        R: IntoLuaMulti;
494}
495
496/// Field registry for [`UserData`] implementors.
497pub trait UserDataFields<T> {
498    /// Add a static field to the [`UserData`].
499    ///
500    /// Static fields are implemented by updating the `__index` metamethod and returning the
501    /// accessed field. This allows them to be used with the expected `userdata.field` syntax.
502    ///
503    /// Static fields are usually shared between all instances of the [`UserData`] of the same type.
504    ///
505    /// If `add_meta_method` is used to set the `__index` metamethod, it will
506    /// be used as a fall-back if no regular field or method are found.
507    fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
508    where
509        V: IntoLua + 'static;
510
511    /// Add a regular field getter as a method which accepts a `&T` as the parameter.
512    ///
513    /// Regular field getters are implemented by overriding the `__index` metamethod and returning
514    /// the accessed field. This allows them to be used with the expected `userdata.field` syntax.
515    ///
516    /// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
517    /// be used as a fall-back if no regular field or method are found.
518    fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
519    where
520        M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
521        R: IntoLua;
522
523    /// Add a regular field setter as a method which accepts a `&mut T` as the first parameter.
524    ///
525    /// Regular field setters are implemented by overriding the `__newindex` metamethod and setting
526    /// the accessed field. This allows them to be used with the expected `userdata.field = value`
527    /// syntax.
528    ///
529    /// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
530    /// will be used as a fall-back if no regular field is found.
531    fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
532    where
533        M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
534        A: FromLua;
535
536    /// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
537    /// argument.
538    fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
539    where
540        F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
541        R: IntoLua;
542
543    /// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
544    /// first argument.
545    fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, function: F)
546    where
547        F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
548        A: FromLua;
549
550    /// Add a metatable field.
551    ///
552    /// This will initialize the metatable field with `value` on [`UserData`] creation.
553    ///
554    /// # Note
555    ///
556    /// `mlua` will trigger an error on an attempt to define a protected metamethod,
557    /// like `__gc` or `__metatable`.
558    fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
559    where
560        V: IntoLua + 'static;
561
562    /// Add a metatable field computed from `f`.
563    ///
564    /// This will initialize the metatable field from `f` on [`UserData`] creation.
565    ///
566    /// # Note
567    ///
568    /// `mlua` will trigger an error on an attempt to define a protected metamethod,
569    /// like `__gc` or `__metatable`.
570    fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
571    where
572        F: FnOnce(&Lua) -> Result<R> + 'static,
573        R: IntoLua;
574}
575
576/// Trait for custom userdata types.
577///
578/// By implementing this trait, a struct becomes eligible for use inside Lua code.
579///
580/// Implementation of [`IntoLua`] is automatically provided, [`FromLua`] needs to be implemented
581/// manually.
582///
583///
584/// # Examples
585///
586/// ```
587/// # use mlua::{Lua, Result, UserData};
588/// # fn main() -> Result<()> {
589/// # let lua = Lua::new();
590/// struct MyUserData;
591///
592/// impl UserData for MyUserData {}
593///
594/// // `MyUserData` now implements `IntoLua`:
595/// lua.globals().set("myobject", MyUserData)?;
596///
597/// lua.load("assert(type(myobject) == 'userdata')").exec()?;
598/// # Ok(())
599/// # }
600/// ```
601///
602/// Custom fields, methods and operators can be provided by implementing `add_fields` or
603/// `add_methods` (refer to [`UserDataFields`] and [`UserDataMethods`] for more information):
604///
605/// ```
606/// # use mlua::{Lua, MetaMethod, Result, UserData, UserDataFields, UserDataMethods};
607/// # fn main() -> Result<()> {
608/// # let lua = Lua::new();
609/// struct MyUserData(i32);
610///
611/// impl UserData for MyUserData {
612///     fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
613///         fields.add_field_method_get("val", |_, this| Ok(this.0));
614///     }
615///
616///     fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
617///         methods.add_method_mut("add", |_, mut this, value: i32| {
618///             this.0 += value;
619///             Ok(())
620///         });
621///
622///         methods.add_meta_method(MetaMethod::Add, |_, this, value: i32| {
623///             Ok(this.0 + value)
624///         });
625///     }
626/// }
627///
628/// lua.globals().set("myobject", MyUserData(123))?;
629///
630/// lua.load(r#"
631///     assert(myobject.val == 123)
632///     myobject:add(7)
633///     assert(myobject.val == 130)
634///     assert(myobject + 10 == 140)
635/// "#).exec()?;
636/// # Ok(())
637/// # }
638/// ```
639pub trait UserData: Sized {
640    /// Adds custom fields specific to this userdata.
641    #[allow(unused_variables)]
642    fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {}
643
644    /// Adds custom methods and operators specific to this userdata.
645    #[allow(unused_variables)]
646    fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {}
647
648    /// Registers this type for use in Lua.
649    ///
650    /// This method is responsible for calling `add_fields` and `add_methods` on the provided
651    /// [`UserDataRegistry`].
652    fn register(registry: &mut UserDataRegistry<Self>) {
653        Self::add_fields(registry);
654        Self::add_methods(registry);
655    }
656}
657
658/// Handle to an internal Lua userdata for any type that implements [`UserData`].
659///
660/// Similar to [`std::any::Any`], this provides an interface for dynamic type checking via the
661/// [`is`] and [`borrow`] methods.
662///
663/// # Note
664///
665/// This API should only be used when necessary. Implementing [`UserData`] already allows defining
666/// methods which check the type and acquire a borrow behind the scenes.
667///
668/// [`is`]: crate::AnyUserData::is
669/// [`borrow`]: crate::AnyUserData::borrow
670#[derive(Clone, Debug, PartialEq)]
671pub struct AnyUserData(pub(crate) ValueRef);
672
673impl AnyUserData {
674    /// Checks whether the type of this userdata is `T`.
675    #[inline]
676    pub fn is<T: 'static>(&self) -> bool {
677        let type_id = self.type_id();
678        // We do not use wrapped types here, rather prefer to check the "real" type of the userdata
679        matches!(type_id, Some(type_id) if type_id == TypeId::of::<T>())
680    }
681
682    /// Borrow this userdata immutably if it is of type `T`.
683    ///
684    /// # Errors
685    ///
686    /// Returns a [`UserDataBorrowError`] if the userdata is already mutably borrowed.
687    /// Returns a [`DataTypeMismatch`] if the userdata is not of type `T` or if it's
688    /// scoped.
689    ///
690    /// [`UserDataBorrowError`]: crate::Error::UserDataBorrowError
691    /// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch
692    #[inline]
693    pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
694        let lua = self.0.lua.lock();
695        unsafe { UserDataRef::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
696    }
697
698    /// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
699    /// to the closure.
700    ///
701    /// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
702    pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
703        let lua = self.0.lua.lock();
704        let type_id = lua.get_userdata_ref_type_id(&self.0)?;
705        let type_hints = TypeIdHints::new::<T>();
706        unsafe { borrow_userdata_scoped(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
707    }
708
709    /// Borrow this userdata mutably if it is of type `T`.
710    ///
711    /// # Errors
712    ///
713    /// Returns a [`UserDataBorrowMutError`] if the userdata cannot be mutably borrowed.
714    /// Returns a [`UserDataTypeMismatch`] if the userdata is not of type `T` or if it's
715    /// scoped.
716    ///
717    /// [`UserDataBorrowMutError`]: crate::Error::UserDataBorrowMutError
718    /// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch
719    #[inline]
720    pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
721        let lua = self.0.lua.lock();
722        unsafe { UserDataRefMut::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
723    }
724
725    /// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
726    /// to the closure.
727    ///
728    /// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
729    pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
730        let lua = self.0.lua.lock();
731        let type_id = lua.get_userdata_ref_type_id(&self.0)?;
732        let type_hints = TypeIdHints::new::<T>();
733        unsafe { borrow_userdata_scoped_mut(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
734    }
735
736    /// Takes the value out of this userdata.
737    ///
738    /// Sets the special "destructed" metatable that prevents any further operations with this
739    /// userdata.
740    ///
741    /// Keeps associated user values unchanged (they will be collected by Lua's GC).
742    pub fn take<T: 'static>(&self) -> Result<T> {
743        let lua = self.0.lua.lock();
744        match lua.get_userdata_ref_type_id(&self.0)? {
745            Some(type_id) if type_id == TypeId::of::<T>() => unsafe {
746                let ref_thread = lua.ref_thread();
747                if (*get_userdata::<UserDataStorage<T>>(ref_thread, self.0.index)).has_exclusive_access() {
748                    take_userdata::<UserDataStorage<T>>(ref_thread, self.0.index).into_inner()
749                } else {
750                    Err(Error::UserDataBorrowMutError)
751                }
752            },
753            _ => Err(Error::UserDataTypeMismatch),
754        }
755    }
756
757    /// Destroys this userdata.
758    ///
759    /// This is similar to [`AnyUserData::take`], but it doesn't require a type.
760    ///
761    /// This method works for non-scoped userdata only.
762    pub fn destroy(&self) -> Result<()> {
763        let lua = self.0.lua.lock();
764        let state = lua.state();
765        unsafe {
766            let _sg = StackGuard::new(state);
767            check_stack(state, 3)?;
768
769            lua.push_userdata_ref(&self.0)?;
770            protect_lua!(state, 1, 1, fn(state) {
771                if ffi::luaL_callmeta(state, -1, cstr!("__gc")) == 0 {
772                    ffi::lua_pushboolean(state, 0);
773                }
774            })?;
775            if ffi::lua_isboolean(state, -1) != 0 && ffi::lua_toboolean(state, -1) != 0 {
776                return Ok(());
777            }
778            Err(Error::UserDataBorrowMutError)
779        }
780    }
781
782    /// Sets an associated value to this [`AnyUserData`].
783    ///
784    /// The value may be any Lua value whatsoever, and can be retrieved with [`user_value`].
785    ///
786    /// This is the same as calling [`set_nth_user_value`] with `n` set to 1.
787    ///
788    /// [`user_value`]: AnyUserData::user_value
789    /// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
790    #[inline]
791    pub fn set_user_value(&self, v: impl IntoLua) -> Result<()> {
792        self.set_nth_user_value(1, v)
793    }
794
795    /// Returns an associated value set by [`set_user_value`].
796    ///
797    /// This is the same as calling [`nth_user_value`] with `n` set to 1.
798    ///
799    /// [`set_user_value`]: AnyUserData::set_user_value
800    /// [`nth_user_value`]: AnyUserData::nth_user_value
801    #[inline]
802    pub fn user_value<V: FromLua>(&self) -> Result<V> {
803        self.nth_user_value(1)
804    }
805
806    /// Sets an associated `n`th value to this [`AnyUserData`].
807    ///
808    /// The value may be any Lua value whatsoever, and can be retrieved with [`nth_user_value`].
809    /// `n` starts from 1 and can be up to 65535.
810    ///
811    /// This is supported for all Lua versions using a wrapping table.
812    ///
813    /// [`nth_user_value`]: AnyUserData::nth_user_value
814    pub fn set_nth_user_value(&self, n: usize, v: impl IntoLua) -> Result<()> {
815        if n < 1 || n > u16::MAX as usize {
816            return Err(Error::runtime("user value index out of bounds"));
817        }
818
819        let lua = self.0.lua.lock();
820        let state = lua.state();
821        unsafe {
822            let _sg = StackGuard::new(state);
823            check_stack(state, 5)?;
824
825            lua.push_userdata_ref(&self.0)?;
826            lua.push(v)?;
827
828            // Multiple (extra) user values are emulated by storing them in a table
829            protect_lua!(state, 2, 0, |state| {
830                if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
831                    // Create a new table to use as uservalue
832                    ffi::lua_pop(state, 1);
833                    ffi::lua_newtable(state);
834                    ffi::lua_pushvalue(state, -1);
835                    ffi::lua_setuservalue(state, -4);
836                }
837                ffi::lua_pushvalue(state, -2);
838                ffi::lua_rawseti(state, -2, n as ffi::lua_Integer);
839            })?;
840
841            Ok(())
842        }
843    }
844
845    /// Returns an associated `n`th value set by [`set_nth_user_value`].
846    ///
847    /// `n` starts from 1 and can be up to 65535.
848    ///
849    /// This is supported for all Lua versions using a wrapping table.
850    ///
851    /// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
852    pub fn nth_user_value<V: FromLua>(&self, n: usize) -> Result<V> {
853        if n < 1 || n > u16::MAX as usize {
854            return Err(Error::runtime("user value index out of bounds"));
855        }
856
857        let lua = self.0.lua.lock();
858        let state = lua.state();
859        unsafe {
860            let _sg = StackGuard::new(state);
861            check_stack(state, 4)?;
862
863            lua.push_userdata_ref(&self.0)?;
864
865            // Multiple (extra) user values are emulated by storing them in a table
866            if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
867                return V::from_lua(Value::Nil, lua.lua());
868            }
869            ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
870
871            V::from_lua(lua.pop_value(), lua.lua())
872        }
873    }
874
875    /// Sets an associated value to this [`AnyUserData`] by name.
876    ///
877    /// The value can be retrieved with [`named_user_value`].
878    ///
879    /// [`named_user_value`]: AnyUserData::named_user_value
880    pub fn set_named_user_value(&self, name: &str, v: impl IntoLua) -> Result<()> {
881        let lua = self.0.lua.lock();
882        let state = lua.state();
883        unsafe {
884            let _sg = StackGuard::new(state);
885            check_stack(state, 5)?;
886
887            lua.push_userdata_ref(&self.0)?;
888            lua.push(v)?;
889
890            // Multiple (extra) user values are emulated by storing them in a table
891            protect_lua!(state, 2, 0, |state| {
892                if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
893                    // Create a new table to use as uservalue
894                    ffi::lua_pop(state, 1);
895                    ffi::lua_newtable(state);
896                    ffi::lua_pushvalue(state, -1);
897                    ffi::lua_setuservalue(state, -4);
898                }
899                ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
900                ffi::lua_pushvalue(state, -3);
901                ffi::lua_rawset(state, -3);
902            })?;
903
904            Ok(())
905        }
906    }
907
908    /// Returns an associated value by name set by [`set_named_user_value`].
909    ///
910    /// [`set_named_user_value`]: AnyUserData::set_named_user_value
911    pub fn named_user_value<V: FromLua>(&self, name: &str) -> Result<V> {
912        let lua = self.0.lua.lock();
913        let state = lua.state();
914        unsafe {
915            let _sg = StackGuard::new(state);
916            check_stack(state, 4)?;
917
918            lua.push_userdata_ref(&self.0)?;
919
920            // Multiple (extra) user values are emulated by storing them in a table
921            if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
922                return V::from_lua(Value::Nil, lua.lua());
923            }
924            push_string(state, name.as_bytes(), !lua.unlikely_memory_error())?;
925            ffi::lua_rawget(state, -2);
926
927            V::from_stack(-1, &lua)
928        }
929    }
930
931    /// Returns a metatable of this [`AnyUserData`].
932    ///
933    /// Returned [`UserDataMetatable`] object wraps the original metatable and
934    /// provides safe access to its methods.
935    ///
936    /// For `T: 'static` returned metatable is shared among all instances of type `T`.
937    #[inline]
938    pub fn metatable(&self) -> Result<UserDataMetatable> {
939        self.raw_metatable().map(UserDataMetatable)
940    }
941
942    /// Returns a raw metatable of this [`AnyUserData`].
943    fn raw_metatable(&self) -> Result<Table> {
944        let lua = self.0.lua.lock();
945        let ref_thread = lua.ref_thread();
946        unsafe {
947            // Check that userdata is registered and not destructed
948            // All registered userdata types have a non-empty metatable
949            let _type_id = lua.get_userdata_ref_type_id(&self.0)?;
950
951            ffi::lua_getmetatable(ref_thread, self.0.index);
952            Ok(Table(lua.pop_ref_thread()))
953        }
954    }
955
956    /// Converts this userdata to a generic C pointer.
957    ///
958    /// There is no way to convert the pointer back to its original value.
959    ///
960    /// Typically this function is used only for hashing and debug information.
961    #[inline]
962    pub fn to_pointer(&self) -> *const c_void {
963        self.0.to_pointer()
964    }
965
966    /// Returns [`TypeId`] of this userdata if it is registered and `'static`.
967    ///
968    /// This method is not available for scoped userdata.
969    #[inline]
970    pub fn type_id(&self) -> Option<TypeId> {
971        let lua = self.0.lua.lock();
972        lua.get_userdata_ref_type_id(&self.0).ok().flatten()
973    }
974
975    /// Returns a type name of this userdata (from a metatable field).
976    ///
977    /// If no type name is set, returns `None`.
978    pub fn type_name(&self) -> Result<Option<StdString>> {
979        let lua = self.0.lua.lock();
980        let state = lua.state();
981        unsafe {
982            let _sg = StackGuard::new(state);
983            check_stack(state, 3)?;
984
985            lua.push_userdata_ref(&self.0)?;
986            let protect = !lua.unlikely_memory_error();
987            let name_type = if protect {
988                protect_lua!(state, 1, 1, |state| {
989                    ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
990                })?
991            } else {
992                ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
993            };
994            match name_type {
995                ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
996                _ => Ok(None),
997            }
998        }
999    }
1000
1001    pub(crate) fn equals(&self, other: &Self) -> Result<bool> {
1002        // Uses lua_rawequal() under the hood
1003        if self == other {
1004            return Ok(true);
1005        }
1006
1007        let mt = self.raw_metatable()?;
1008        if mt != other.raw_metatable()? {
1009            return Ok(false);
1010        }
1011
1012        if mt.contains_key("__eq")? {
1013            return mt.get::<Function>("__eq")?.call((self, other));
1014        }
1015
1016        Ok(false)
1017    }
1018
1019    /// Returns `true` if this [`AnyUserData`] is serializable (e.g. was created using
1020    /// [`Lua::create_ser_userdata`]).
1021    #[cfg(feature = "serde")]
1022    pub(crate) fn is_serializable(&self) -> bool {
1023        let lua = self.0.lua.lock();
1024        let is_serializable = || unsafe {
1025            // Userdata must be registered and not destructed
1026            let _ = lua.get_userdata_ref_type_id(&self.0)?;
1027            let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
1028            Ok::<_, Error>((*ud).is_serializable())
1029        };
1030        is_serializable().unwrap_or(false)
1031    }
1032}
1033
1034/// Handle to a [`AnyUserData`] metatable.
1035#[derive(Clone, Debug)]
1036pub struct UserDataMetatable(pub(crate) Table);
1037
1038impl UserDataMetatable {
1039    /// Gets the value associated to `key` from the metatable.
1040    ///
1041    /// If no value is associated to `key`, returns the `Nil` value.
1042    /// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
1043    pub fn get<V: FromLua>(&self, key: impl AsRef<str>) -> Result<V> {
1044        self.0.raw_get(MetaMethod::validate(key.as_ref())?)
1045    }
1046
1047    /// Sets a key-value pair in the metatable.
1048    ///
1049    /// If the value is `Nil`, this will effectively remove the `key`.
1050    /// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
1051    /// Setting `__index` or `__newindex` metamethods is also restricted because their values are
1052    /// cached for `mlua` internal usage.
1053    pub fn set(&self, key: impl AsRef<str>, value: impl IntoLua) -> Result<()> {
1054        let key = MetaMethod::validate(key.as_ref())?;
1055        // `__index` and `__newindex` cannot be changed in runtime, because values are cached
1056        if key == MetaMethod::Index || key == MetaMethod::NewIndex {
1057            return Err(Error::MetaMethodRestricted(key.to_string()));
1058        }
1059        self.0.raw_set(key, value)
1060    }
1061
1062    /// Checks whether the metatable contains a non-nil value for `key`.
1063    pub fn contains(&self, key: impl AsRef<str>) -> Result<bool> {
1064        self.0.contains_key(MetaMethod::validate(key.as_ref())?)
1065    }
1066
1067    /// Returns an iterator over the pairs of the metatable.
1068    ///
1069    /// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
1070    ///
1071    /// [`Result`]: crate::Result
1072    pub fn pairs<V: FromLua>(&self) -> UserDataMetatablePairs<'_, V> {
1073        UserDataMetatablePairs(self.0.pairs())
1074    }
1075}
1076
1077/// An iterator over the pairs of a [`AnyUserData`] metatable.
1078///
1079/// It skips restricted metamethods, such as `__gc` or `__metatable`.
1080///
1081/// This struct is created by the [`UserDataMetatable::pairs`] method.
1082pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, StdString, V>);
1083
1084impl<V> Iterator for UserDataMetatablePairs<'_, V>
1085where
1086    V: FromLua,
1087{
1088    type Item = Result<(StdString, V)>;
1089
1090    fn next(&mut self) -> Option<Self::Item> {
1091        loop {
1092            match self.0.next()? {
1093                Ok((key, value)) => {
1094                    // Skip restricted metamethods
1095                    if MetaMethod::validate(&key).is_ok() {
1096                        break Some(Ok((key, value)));
1097                    }
1098                }
1099                Err(e) => break Some(Err(e)),
1100            }
1101        }
1102    }
1103}
1104
1105#[cfg(feature = "serde")]
1106impl Serialize for AnyUserData {
1107    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
1108    where
1109        S: Serializer,
1110    {
1111        let lua = self.0.lua.lock();
1112        unsafe {
1113            let _ = lua
1114                .get_userdata_ref_type_id(&self.0)
1115                .map_err(ser::Error::custom)?;
1116            let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
1117            ud.serialize(serializer)
1118        }
1119    }
1120}
1121
1122struct WrappedUserdata<F: FnOnce(&Lua) -> Result<AnyUserData>>(F);
1123
1124impl AnyUserData {
1125    /// Wraps any Rust type, returning an opaque type that implements [`IntoLua`] trait.
1126    ///
1127    /// This function uses [`Lua::create_any_userdata`] under the hood.
1128    pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua {
1129        WrappedUserdata(move |lua| lua.create_any_userdata(data))
1130    }
1131
1132    /// Wraps any Rust type that implements [`Serialize`], returning an opaque type that implements
1133    /// [`IntoLua`] trait.
1134    ///
1135    /// This function uses [`Lua::create_ser_any_userdata`] under the hood.
1136    #[cfg(feature = "serde")]
1137    #[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
1138    pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
1139        WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
1140    }
1141}
1142
1143impl<F> IntoLua for WrappedUserdata<F>
1144where
1145    F: for<'l> FnOnce(&'l Lua) -> Result<AnyUserData>,
1146{
1147    fn into_lua(self, lua: &Lua) -> Result<Value> {
1148        (self.0)(lua).map(Value::UserData)
1149    }
1150}
1151
1152mod cell;
1153mod lock;
1154mod object;
1155mod r#ref;
1156mod registry;
1157mod util;
1158
1159#[cfg(test)]
1160mod assertions {
1161    use super::*;
1162
1163    #[cfg(not(feature = "send"))]
1164    static_assertions::assert_not_impl_any!(AnyUserData: Send);
1165    #[cfg(feature = "send")]
1166    static_assertions::assert_impl_all!(AnyUserData: Send, Sync);
1167}