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}