mlua_codemp_patch/
userdata.rs

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