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}