use std::any::{type_name, TypeId};
use std::cell::{Ref, RefCell, RefMut};
use std::ffi::CStr;
use std::fmt;
use std::hash::Hash;
use std::mem;
use std::ops::{Deref, DerefMut};
use std::os::raw::{c_char, c_int, c_void};
use std::string::String as StdString;
#[cfg(feature = "async")]
use std::future::Future;
#[cfg(feature = "serialize")]
use {
serde::ser::{self, Serialize, Serializer},
std::result::Result as StdResult,
};
use crate::error::{Error, Result};
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::types::{LuaRef, MaybeSend, SubtypeId};
use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use crate::UserDataRegistry;
#[cfg(feature = "lua54")]
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum MetaMethod {
Add,
Sub,
Mul,
Div,
Mod,
Pow,
Unm,
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
IDiv,
#[cfg(any(feature = "lua54", feature = "lua53"))]
BAnd,
#[cfg(any(feature = "lua54", feature = "lua53"))]
BOr,
#[cfg(any(feature = "lua54", feature = "lua53"))]
BXor,
#[cfg(any(feature = "lua54", feature = "lua53"))]
BNot,
#[cfg(any(feature = "lua54", feature = "lua53"))]
Shl,
#[cfg(any(feature = "lua54", feature = "lua53"))]
Shr,
Concat,
Len,
Eq,
Lt,
Le,
Index,
NewIndex,
Call,
ToString,
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52",
))]
Pairs,
#[cfg(any(feature = "lua52", feature = "luajit52", doc))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua52", feature = "luajit52"))))]
IPairs,
#[cfg(any(feature = "luau", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
Iter,
#[cfg(feature = "lua54")]
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
Close,
#[doc(hidden)]
Type,
}
impl PartialEq<MetaMethod> for &str {
fn eq(&self, other: &MetaMethod) -> bool {
*self == other.name()
}
}
impl PartialEq<MetaMethod> for StdString {
fn eq(&self, other: &MetaMethod) -> bool {
self == other.name()
}
}
impl fmt::Display for MetaMethod {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{}", self.name())
}
}
impl MetaMethod {
pub const fn name(self) -> &'static str {
match self {
MetaMethod::Add => "__add",
MetaMethod::Sub => "__sub",
MetaMethod::Mul => "__mul",
MetaMethod::Div => "__div",
MetaMethod::Mod => "__mod",
MetaMethod::Pow => "__pow",
MetaMethod::Unm => "__unm",
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
MetaMethod::IDiv => "__idiv",
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BAnd => "__band",
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BOr => "__bor",
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BXor => "__bxor",
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::BNot => "__bnot",
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::Shl => "__shl",
#[cfg(any(feature = "lua54", feature = "lua53"))]
MetaMethod::Shr => "__shr",
MetaMethod::Concat => "__concat",
MetaMethod::Len => "__len",
MetaMethod::Eq => "__eq",
MetaMethod::Lt => "__lt",
MetaMethod::Le => "__le",
MetaMethod::Index => "__index",
MetaMethod::NewIndex => "__newindex",
MetaMethod::Call => "__call",
MetaMethod::ToString => "__tostring",
#[cfg(any(
feature = "lua54",
feature = "lua53",
feature = "lua52",
feature = "luajit52"
))]
MetaMethod::Pairs => "__pairs",
#[cfg(any(feature = "lua52", feature = "luajit52"))]
MetaMethod::IPairs => "__ipairs",
#[cfg(feature = "luau")]
MetaMethod::Iter => "__iter",
#[cfg(feature = "lua54")]
MetaMethod::Close => "__close",
#[rustfmt::skip]
MetaMethod::Type => if cfg!(feature = "luau") { "__type" } else { "__name" },
}
}
pub(crate) const fn as_cstr(self) -> &'static CStr {
match self {
#[rustfmt::skip]
MetaMethod::Type => unsafe {
CStr::from_bytes_with_nul_unchecked(if cfg!(feature = "luau") { b"__type\0" } else { b"__name\0" })
},
_ => unreachable!(),
}
}
pub(crate) fn validate(name: &str) -> Result<&str> {
match name {
"__gc" => Err(Error::MetaMethodRestricted(name.to_string())),
"__metatable" => Err(Error::MetaMethodRestricted(name.to_string())),
_ if name.starts_with("__mlua") => Err(Error::MetaMethodRestricted(name.to_string())),
name => Ok(name),
}
}
}
impl AsRef<str> for MetaMethod {
fn as_ref(&self) -> &str {
self.name()
}
}
pub trait UserDataMethods<'lua, T> {
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
#[cfg(feature = "async")]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>;
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, name: impl AsRef<str>, method: M)
where
'lua: 's,
T: 'static,
M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
MR: Future<Output = Result<R>> + 's,
R: IntoLuaMulti<'lua>;
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
R: IntoLuaMulti<'lua>;
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti<'lua>,
FR: Future<Output = Result<R>> + 'lua,
R: IntoLuaMulti<'lua>;
#[doc(hidden)]
fn append_methods_from<S>(&mut self, _other: UserDataRegistry<'lua, S>) {}
}
pub trait UserDataFields<'lua, T> {
fn add_field<V>(&mut self, name: impl AsRef<str>, value: V)
where
V: IntoLua<'lua> + Clone + 'static;
fn add_field_method_get<M, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>;
fn add_field_method_set<M, A>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
A: FromLua<'lua>;
fn add_field_function_get<F, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>;
fn add_field_function_set<F, A>(&mut self, name: impl AsRef<str>, function: F)
where
F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static,
A: FromLua<'lua>;
fn add_meta_field<V>(&mut self, name: impl AsRef<str>, value: V)
where
V: IntoLua<'lua> + Clone + 'static;
fn add_meta_field_with<F, R>(&mut self, name: impl AsRef<str>, f: F)
where
F: Fn(&'lua Lua) -> Result<R> + MaybeSend + 'static,
R: IntoLua<'lua>;
#[doc(hidden)]
fn append_fields_from<S>(&mut self, _other: UserDataRegistry<'lua, S>) {}
}
pub trait UserData: Sized {
#[allow(unused_variables)]
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {}
#[allow(unused_variables)]
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {}
}
pub(crate) struct UserDataCell<T>(RefCell<UserDataVariant<T>>);
impl<T> UserDataCell<T> {
#[inline]
pub(crate) fn new(data: T) -> Self {
UserDataCell(RefCell::new(UserDataVariant::new(data)))
}
#[inline]
pub(crate) fn new_ref(data: &T) -> Self {
UserDataCell(RefCell::new(UserDataVariant::new_ref(data)))
}
#[inline]
pub(crate) fn new_ref_mut(data: &mut T) -> Self {
UserDataCell(RefCell::new(UserDataVariant::new_ref_mut(data)))
}
#[cfg(feature = "serialize")]
#[inline]
pub(crate) fn new_ser(data: T) -> Self
where
T: Serialize + 'static,
{
UserDataCell(RefCell::new(UserDataVariant::new_ser(data)))
}
#[inline]
pub(crate) fn try_borrow(&self) -> Result<Ref<T>> {
self.0
.try_borrow()
.map(|r| Ref::map(r, |r| r.deref()))
.map_err(|_| Error::UserDataBorrowError)
}
#[inline]
pub(crate) fn try_borrow_mut(&self) -> Result<RefMut<T>> {
self.0
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)
.and_then(|r| {
RefMut::filter_map(r, |r| r.try_deref_mut().ok())
.map_err(|_| Error::UserDataBorrowMutError)
})
}
#[inline]
fn into_inner(self) -> Result<T> {
self.0.into_inner().into_inner()
}
}
pub(crate) enum UserDataVariant<T> {
Default(Box<T>),
Ref(*const T),
RefMut(*mut T),
#[cfg(feature = "serialize")]
Serializable(Box<dyn erased_serde::Serialize>),
}
impl<T> UserDataVariant<T> {
#[inline]
fn new(data: T) -> Self {
UserDataVariant::Default(Box::new(data))
}
#[inline]
fn new_ref(data: &T) -> Self {
UserDataVariant::Ref(data)
}
#[inline]
fn new_ref_mut(data: &mut T) -> Self {
UserDataVariant::RefMut(data)
}
#[cfg(feature = "serialize")]
#[inline]
fn new_ser(data: T) -> Self
where
T: Serialize + 'static,
{
UserDataVariant::Serializable(Box::new(data))
}
#[inline]
fn try_deref_mut(&mut self) -> Result<&mut T> {
match self {
Self::Default(data) => Ok(data.deref_mut()),
Self::Ref(_) => Err(Error::UserDataBorrowMutError),
Self::RefMut(data) => unsafe { Ok(&mut **data) },
#[cfg(feature = "serialize")]
Self::Serializable(data) => unsafe { Ok(&mut *(data.as_mut() as *mut _ as *mut T)) },
}
}
#[inline]
fn into_inner(self) -> Result<T> {
match self {
Self::Default(data) => Ok(*data),
Self::Ref(_) | Self::RefMut(_) => Err(Error::UserDataTypeMismatch),
#[cfg(feature = "serialize")]
Self::Serializable(data) => unsafe {
Ok(*Box::from_raw(Box::into_raw(data) as *mut T))
},
}
}
}
impl<T> Deref for UserDataVariant<T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
match self {
Self::Default(data) => data,
Self::Ref(data) => unsafe { &**data },
Self::RefMut(data) => unsafe { &**data },
#[cfg(feature = "serialize")]
Self::Serializable(data) => unsafe {
&*(data.as_ref() as *const _ as *const Self::Target)
},
}
}
}
#[derive(Clone, Debug)]
pub struct AnyUserData<'lua>(pub(crate) LuaRef<'lua>, pub(crate) SubtypeId);
#[cfg(feature = "unstable")]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
#[derive(Clone, Debug)]
pub struct OwnedAnyUserData(pub(crate) crate::types::LuaOwnedRef, pub(crate) SubtypeId);
#[cfg(feature = "unstable")]
impl OwnedAnyUserData {
#[cfg_attr(feature = "send", allow(unused))]
pub const fn to_ref(&self) -> AnyUserData {
AnyUserData(self.0.to_ref(), self.1)
}
}
impl<'lua> AnyUserData<'lua> {
pub fn is<T: 'static>(&self) -> bool {
self.inspect(|_: &UserDataCell<T>| Ok(())).is_ok()
}
#[inline]
pub fn borrow<T: 'static>(&self) -> Result<Ref<T>> {
self.inspect(|cell| cell.try_borrow())
}
#[inline]
pub fn borrow_mut<T: 'static>(&self) -> Result<RefMut<T>> {
self.inspect(|cell| cell.try_borrow_mut())
}
pub fn take<T: 'static>(&self) -> Result<T> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 2)?;
let type_id = lua.push_userdata_ref(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let _ = (*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataCell<T>>(state).into_inner()
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
#[inline]
pub fn set_user_value<V: IntoLua<'lua>>(&self, v: V) -> Result<()> {
self.set_nth_user_value(1, v)
}
#[inline]
pub fn user_value<V: FromLua<'lua>>(&self) -> Result<V> {
self.nth_user_value(1)
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `user_value` instead")]
pub fn get_user_value<V: FromLua<'lua>>(&self) -> Result<V> {
self.nth_user_value(1)
}
pub fn set_nth_user_value<V: IntoLua<'lua>>(&self, n: usize, v: V) -> Result<()> {
if n < 1 || n > u16::MAX as usize {
return Err(Error::runtime("user value index out of bounds"));
}
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_userdata_ref(&self.0)?;
lua.push(v)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_setiuservalue(state, -2, n as c_int);
return Ok(());
}
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushvalue(state, -2);
#[cfg(feature = "lua54")]
ffi::lua_rawseti(state, -2, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawseti(state, -2, n as ffi::lua_Integer);
})?;
Ok(())
}
}
pub fn nth_user_value<V: FromLua<'lua>>(&self, n: usize) -> Result<V> {
if n < 1 || n > u16::MAX as usize {
return Err(Error::runtime("user value index out of bounds"));
}
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
lua.push_userdata_ref(&self.0)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_getiuservalue(state, -1, n as c_int);
return V::from_lua(lua.pop_value(), lua);
}
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
#[cfg(feature = "lua54")]
ffi::lua_rawgeti(state, -1, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
})?;
V::from_lua(lua.pop_value(), lua)
}
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `nth_user_value` instead")]
pub fn get_nth_user_value<V: FromLua<'lua>>(&self, n: usize) -> Result<V> {
self.nth_user_value(n)
}
pub fn set_named_user_value<V: IntoLua<'lua>>(&self, name: &str, v: V) -> Result<()> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 5)?;
lua.push_userdata_ref(&self.0)?;
lua.push(v)?;
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
ffi::lua_pushvalue(state, -3);
ffi::lua_rawset(state, -3);
})?;
Ok(())
}
}
pub fn named_user_value<V: FromLua<'lua>>(&self, name: &str) -> Result<V> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
lua.push_userdata_ref(&self.0)?;
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
ffi::lua_rawget(state, -2);
})?;
V::from_lua(lua.pop_value(), lua)
}
}
#[doc(hidden)]
#[deprecated(since = "0.9.0", note = "please use `named_user_value` instead")]
pub fn get_named_user_value<V: FromLua<'lua>>(&self, name: &str) -> Result<V> {
self.named_user_value(name)
}
#[inline]
pub fn get_metatable(&self) -> Result<UserDataMetatable<'lua>> {
self.get_raw_metatable().map(UserDataMetatable)
}
fn get_raw_metatable(&self) -> Result<Table<'lua>> {
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_userdata_ref(&self.0)?;
ffi::lua_getmetatable(state, -1); Ok(Table(lua.pop_ref()))
}
}
#[inline]
pub fn to_pointer(&self) -> *const c_void {
self.0.to_pointer()
}
#[cfg(all(feature = "unstable", any(not(feature = "send"), doc)))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "unstable", not(feature = "send")))))]
#[inline]
pub fn into_owned(self) -> OwnedAnyUserData {
OwnedAnyUserData(self.0.into_owned(), self.1)
}
#[cfg(feature = "async")]
#[inline]
pub(crate) fn type_id(&self) -> Result<Option<TypeId>> {
unsafe { self.0.lua.get_userdata_ref_type_id(&self.0) }
}
pub(crate) fn type_name(&self) -> Result<Option<StdString>> {
match self.1 {
SubtypeId::None => {}
#[cfg(feature = "luau")]
SubtypeId::Buffer => return Ok(Some("buffer".to_owned())),
#[cfg(feature = "luajit")]
SubtypeId::CData => return Ok(Some("cdata".to_owned())),
}
let lua = self.0.lua;
let state = lua.state();
unsafe {
let _sg = StackGuard::new(state);
check_stack(state, 3)?;
lua.push_userdata_ref(&self.0)?;
let protect = !lua.unlikely_memory_error();
let name_type = if protect {
protect_lua!(state, 1, 1, |state| {
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
})?
} else {
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
};
match name_type {
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
_ => Ok(None),
}
}
}
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
let other = other.as_ref();
if self == other {
return Ok(true);
}
let mt = self.get_raw_metatable()?;
if mt != other.get_raw_metatable()? {
return Ok(false);
}
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
}
Ok(false)
}
#[cfg(feature = "serialize")]
pub(crate) fn is_serializable(&self) -> bool {
let lua = self.0.lua;
let is_serializable = || unsafe {
let _ = lua.get_userdata_ref_type_id(&self.0)?;
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
match &*ud.0.try_borrow().map_err(|_| Error::UserDataBorrowError)? {
UserDataVariant::Serializable(_) => Result::Ok(true),
_ => Result::Ok(false),
}
};
is_serializable().unwrap_or(false)
}
fn inspect<'a, T, F, R>(&'a self, func: F) -> Result<R>
where
T: 'static,
F: FnOnce(&'a UserDataCell<T>) -> Result<R>,
{
let lua = self.0.lua;
unsafe {
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let ref_thread = lua.ref_thread();
func(&*get_userdata::<UserDataCell<T>>(ref_thread, self.0.index))
}
_ => Err(Error::UserDataTypeMismatch),
}
}
}
}
impl<'lua> PartialEq for AnyUserData<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'lua> AsRef<AnyUserData<'lua>> for AnyUserData<'lua> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
unsafe fn getuservalue_table(state: *mut ffi::lua_State, idx: c_int) -> c_int {
#[cfg(feature = "lua54")]
return ffi::lua_getiuservalue(state, idx, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
return ffi::lua_getuservalue(state, idx);
}
#[cfg(feature = "unstable")]
impl OwnedAnyUserData {
#[inline]
pub fn borrow<T: 'static>(&self) -> Result<Ref<T>> {
let ud = self.to_ref();
let t = ud.borrow::<T>()?;
Ok(unsafe { mem::transmute::<Ref<T>, Ref<T>>(t) })
}
#[inline]
pub fn borrow_mut<T: 'static>(&self) -> Result<RefMut<T>> {
let ud = self.to_ref();
let t = ud.borrow_mut::<T>()?;
Ok(unsafe { mem::transmute::<RefMut<T>, RefMut<T>>(t) })
}
#[inline]
pub fn take<T: 'static>(&self) -> Result<T> {
self.to_ref().take()
}
}
#[derive(Clone, Debug)]
pub struct UserDataMetatable<'lua>(pub(crate) Table<'lua>);
impl<'lua> UserDataMetatable<'lua> {
pub fn get<V: FromLua<'lua>>(&self, key: impl AsRef<str>) -> Result<V> {
self.0.raw_get(MetaMethod::validate(key.as_ref())?)
}
pub fn set<V: IntoLua<'lua>>(&self, key: impl AsRef<str>, value: V) -> Result<()> {
let key = MetaMethod::validate(key.as_ref())?;
if key == MetaMethod::Index || key == MetaMethod::NewIndex {
return Err(Error::MetaMethodRestricted(key.to_string()));
}
self.0.raw_set(key, value)
}
pub fn contains(&self, key: impl AsRef<str>) -> Result<bool> {
self.0.contains_key(MetaMethod::validate(key.as_ref())?)
}
pub fn pairs<V: FromLua<'lua>>(self) -> UserDataMetatablePairs<'lua, V> {
UserDataMetatablePairs(self.0.pairs())
}
}
pub struct UserDataMetatablePairs<'lua, V>(TablePairs<'lua, StdString, V>);
impl<'lua, V> Iterator for UserDataMetatablePairs<'lua, V>
where
V: FromLua<'lua>,
{
type Item = Result<(StdString, V)>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.0.next()? {
Ok((key, value)) => {
if MetaMethod::validate(&key).is_ok() {
break Some(Ok((key, value)));
}
}
Err(e) => break Some(Err(e)),
}
}
}
}
#[cfg(feature = "serialize")]
impl<'lua> Serialize for AnyUserData<'lua> {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: Serializer,
{
let lua = self.0.lua;
#[cfg(feature = "luau")]
if self.1 == SubtypeId::Buffer {
let buf = unsafe {
let mut size = 0usize;
let buf = ffi::lua_tobuffer(lua.ref_thread(), self.0.index, &mut size);
mlua_assert!(!buf.is_null(), "invalid Luau buffer");
std::slice::from_raw_parts(buf as *const u8, size)
};
return serializer.serialize_bytes(buf);
}
let data = unsafe {
let _ = lua
.get_userdata_ref_type_id(&self.0)
.map_err(ser::Error::custom)?;
let ud = &*get_userdata::<UserDataCell<()>>(lua.ref_thread(), self.0.index);
ud.0.try_borrow()
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
};
match &*data {
UserDataVariant::Serializable(ser) => ser.serialize(serializer),
_ => Err(ser::Error::custom("cannot serialize <userdata>")),
}
}
}
pub struct UserDataRef<'lua, T: 'static>(#[allow(unused)] AnyUserData<'lua>, Ref<'lua, T>);
impl<'lua, T: 'static> Deref for UserDataRef<'lua, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<'lua, T: 'static> UserDataRef<'lua, T> {
pub(crate) fn from_value(value: Value<'lua>) -> Result<Self> {
let ud = try_value_to_userdata::<T>(value)?;
let this = unsafe { mem::transmute(ud.borrow::<T>()?) };
Ok(UserDataRef(ud, this))
}
}
pub struct UserDataRefMut<'lua, T: 'static>(#[allow(unused)] AnyUserData<'lua>, RefMut<'lua, T>);
impl<'lua, T: 'static> Deref for UserDataRefMut<'lua, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
impl<'lua, T: 'static> DerefMut for UserDataRefMut<'lua, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.1
}
}
impl<'lua, T: 'static> UserDataRefMut<'lua, T> {
pub(crate) fn from_value(value: Value<'lua>) -> Result<Self> {
let ud = try_value_to_userdata::<T>(value)?;
let this = unsafe { mem::transmute(ud.borrow_mut::<T>()?) };
Ok(UserDataRefMut(ud, this))
}
}
pub(crate) struct WrappedUserdata<F: for<'lua> FnOnce(&'lua Lua) -> Result<AnyUserData<'lua>>>(F);
impl<'lua> AnyUserData<'lua> {
pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua<'lua> {
WrappedUserdata(move |lua| lua.create_any_userdata(data))
}
}
impl<'lua, F> IntoLua<'lua> for WrappedUserdata<F>
where
F: for<'l> FnOnce(&'l Lua) -> Result<AnyUserData<'l>>,
{
fn into_lua(self, lua: &'lua Lua) -> Result<Value<'lua>> {
(self.0)(lua).map(Value::UserData)
}
}
#[inline]
fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
match value {
Value::UserData(ud) => Ok(ud),
_ => Err(Error::FromLuaConversionError {
from: value.type_name(),
to: "userdata",
message: Some(format!("expected userdata of type {}", type_name::<T>())),
}),
}
}
#[cfg(test)]
mod assertions {
use super::*;
static_assertions::assert_not_impl_any!(AnyUserData: Send);
#[cfg(all(feature = "unstable", not(feature = "send")))]
static_assertions::assert_not_impl_any!(OwnedAnyUserData: Send);
}