diff --git a/src/lua.rs b/src/lua.rs index 6fec30b..0c65c88 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -6,7 +6,7 @@ use std::fmt; use std::marker::PhantomData; use std::os::raw::{c_char, c_int, c_void}; use std::panic::resume_unwind; -use std::sync::{Arc, Mutex, MutexGuard, Weak}; +use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak}; use std::{mem, ptr, str}; use crate::error::{Error, Result}; @@ -32,6 +32,9 @@ use crate::util::{ }; use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; +#[cfg(not(feature = "send"))] +use std::rc::Rc; + #[cfg(feature = "async")] use { crate::types::AsyncCallback, @@ -1607,6 +1610,11 @@ impl Lua { self.push_value(f(self)?)?; ffi::safe::lua_rawsetfield(self.state, -2, k.validate()?.name())?; } + // Add special `__mlua_type_id` field + let type_id_ptr = + ffi::safe::lua_newuserdata(self.state, mem::size_of::())? as *mut TypeId; + ptr::write(type_id_ptr, type_id); + ffi::safe::lua_rawsetfield(self.state, -2, "__mlua_type_id")?; let metatable_index = ffi::lua_absindex(self.state, -1); let mut extra_tables_count = 0; @@ -1690,7 +1698,7 @@ impl Lua { // Pushes a LuaRef value onto the stack, checking that it's a registered // and not destructed UserData. // Uses 3 stack spaces, does not call checkstack. - pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> { + pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef, with_mt: bool) -> Result<()> { self.push_ref(lref); if ffi::lua_getmetatable(self.state, -1) == 0 { return Err(Error::UserDataTypeMismatch); @@ -1699,7 +1707,9 @@ impl Lua { let ptr = ffi::lua_topointer(self.state, -1); let extra = mlua_expect!(self.extra.lock(), "extra is poisoned"); if extra.registered_userdata_mt.contains(&(ptr as isize)) { - ffi::lua_pop(self.state, 1); + if !with_mt { + ffi::lua_pop(self.state, 1); + } return Ok(()); } // Maybe userdata was destructed? @@ -2488,6 +2498,21 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet self.meta_methods .push((meta.into(), Self::box_function_mut(function))); } + + // Below are internal methods used in generated code + + fn add_callback(&mut self, name: Vec, callback: Callback<'lua, 'static>) { + self.methods.push((name, callback)); + } + + #[cfg(feature = "async")] + fn add_async_callback(&mut self, name: Vec, callback: AsyncCallback<'lua, 'static>) { + self.async_methods.push((name, callback)); + } + + fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) { + self.meta_methods.push((meta, callback)); + } } impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { @@ -2500,8 +2525,29 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { Box::new(move |lua, mut args| { if let Some(front) = args.pop_front() { let userdata = AnyUserData::from_lua(front, lua)?; - let userdata = userdata.borrow::()?; - method(lua, &userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + match userdata.type_id()? { + id if id == TypeId::of::() => { + let ud = userdata.borrow::()?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + #[cfg(not(feature = "send"))] + id if id == TypeId::of::>>() => { + let ud = userdata.borrow::>>()?; + let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + id if id == TypeId::of::>>() => { + let ud = userdata.borrow::>>()?; + let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + id if id == TypeId::of::>>() => { + let ud = userdata.borrow::>>()?; + let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; + method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + _ => Err(Error::UserDataTypeMismatch), + } } else { Err(Error::FromLuaConversionError { from: "missing argument", @@ -2522,11 +2568,34 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { Box::new(move |lua, mut args| { if let Some(front) = args.pop_front() { let userdata = AnyUserData::from_lua(front, lua)?; - let mut userdata = userdata.borrow_mut::()?; let mut method = method .try_borrow_mut() .map_err(|_| Error::RecursiveMutCallback)?; - (&mut *method)(lua, &mut userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + match userdata.type_id()? { + id if id == TypeId::of::() => { + let mut ud = userdata.borrow_mut::()?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + #[cfg(not(feature = "send"))] + id if id == TypeId::of::>>() => { + let ud = userdata.borrow::>>()?; + let mut ud = ud + .try_borrow_mut() + .map_err(|_| Error::UserDataBorrowMutError)?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + id if id == TypeId::of::>>() => { + let ud = userdata.borrow::>>()?; + let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + id if id == TypeId::of::>>() => { + let ud = userdata.borrow::>>()?; + let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?; + method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } + _ => Err(Error::UserDataTypeMismatch), + } } else { Err(Error::FromLuaConversionError { from: "missing argument", @@ -2709,4 +2778,51 @@ impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFiel }), )); } + + // Below are internal methods + + fn add_field_getter(&mut self, name: Vec, callback: Callback<'lua, 'static>) { + self.field_getters.push((name, callback)); + } + + fn add_field_setter(&mut self, name: Vec, callback: Callback<'lua, 'static>) { + self.field_setters.push((name, callback)); + } } + +macro_rules! lua_userdata_impl { + ($type:ty) => { + impl UserData for $type { + fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { + let mut orig_fields = StaticUserDataFields::default(); + T::add_fields(&mut orig_fields); + for (name, callback) in orig_fields.field_getters { + fields.add_field_getter(name, callback); + } + for (name, callback) in orig_fields.field_setters { + fields.add_field_setter(name, callback); + } + } + + fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + let mut orig_methods = StaticUserDataMethods::default(); + T::add_methods(&mut orig_methods); + for (name, callback) in orig_methods.methods { + methods.add_callback(name, callback); + } + #[cfg(feature = "async")] + for (name, callback) in orig_methods.async_methods { + methods.add_async_callback(name, callback); + } + for (meta, callback) in orig_methods.meta_methods { + methods.add_meta_callback(meta, callback); + } + } + } + }; +} + +#[cfg(not(feature = "send"))] +lua_userdata_impl!(Rc>); +lua_userdata_impl!(Arc>); +lua_userdata_impl!(Arc>); diff --git a/src/scope.rs b/src/scope.rs index c7a3e48..fa5bcc2 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,8 +1,9 @@ use std::any::Any; -use std::cell::{Cell, Ref, RefCell, RefMut}; +use std::cell::{Cell, RefCell}; use std::marker::PhantomData; use std::mem; use std::os::raw::{c_int, c_void}; +use std::rc::Rc; #[cfg(feature = "serialize")] use serde::Serialize; @@ -238,7 +239,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { where T: 'scope + UserData, { - let data = UserDataCell::new_arc(data); + let data = Rc::new(RefCell::new(data)); // 'callback outliving 'scope is a lie to make the types work out, required due to the // inability to work with the more correct callback type that is universally quantified over @@ -247,7 +248,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { // parameters. fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>( scope: &Scope<'lua, 'scope>, - data: UserDataCell, + data: Rc>, data_ptr: *mut c_void, method: NonStaticMethod<'callback, T>, ) -> Result> { @@ -263,7 +264,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { unsafe { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 3)?; - lua.push_userdata_ref(&ud.0)?; + lua.push_userdata_ref(&ud.0, false)?; if get_userdata(lua.state, -1) == data_ptr { return Ok(()); } @@ -276,10 +277,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { NonStaticMethod::Method(method) => { let f = Box::new(move |lua, mut args: MultiValue<'callback>| { check_ud_type(lua, args.pop_front())?; - let data = data - .try_borrow() - .map(|cell| Ref::map(cell, AsRef::as_ref)) - .map_err(|_| Error::UserDataBorrowError)?; + let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?; method(lua, &*data, args) }); unsafe { scope.create_callback(f) } @@ -293,7 +291,6 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { .map_err(|_| Error::RecursiveMutCallback)?; let mut data = data .try_borrow_mut() - .map(|cell| RefMut::map(cell, AsMut::as_mut)) .map_err(|_| Error::UserDataBorrowMutError)?; (&mut *method)(lua, &mut *data, args) }); @@ -324,7 +321,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 13)?; - push_userdata(lua.state, data.clone())?; + push_userdata(lua.state, UserDataCell::new(data.clone()))?; let data_ptr = ffi::lua_touserdata(lua.state, -1); // Prepare metatable, add meta methods first and then meta fields @@ -379,7 +376,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { methods_index = Some(ffi::lua_absindex(lua.state, -1)); } - init_userdata_metatable::<()>( + init_userdata_metatable::>>>( lua.state, metatable_index, field_getters_index, @@ -427,7 +424,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { mem::transmute(f) } - vec![Box::new(seal(take_userdata::>(state)))] + let ud = Box::new(seal(take_userdata::>>>(state))); + vec![ud] }); self.destructors .borrow_mut() diff --git a/src/userdata.rs b/src/userdata.rs index 6b84795..b9bb302 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -1,9 +1,9 @@ +use std::any::TypeId; use std::cell::{Ref, RefCell, RefMut}; use std::fmt; use std::hash::{Hash, Hasher}; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use std::string::String as StdString; -use std::sync::Arc; #[cfg(feature = "async")] use std::future::Future; @@ -19,13 +19,16 @@ use crate::ffi; use crate::function::Function; use crate::lua::Lua; use crate::table::{Table, TablePairs}; -use crate::types::{LuaRef, MaybeSend}; +use crate::types::{Callback, LuaRef, MaybeSend}; use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard}; use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; #[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))] use crate::value::Value; +#[cfg(feature = "async")] +use crate::types::AsyncCallback; + /// Kinds of metamethods that can be overridden. /// /// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is @@ -403,6 +406,20 @@ pub trait UserDataMethods<'lua, T: UserData> { A: FromLuaMulti<'lua>, R: ToLuaMulti<'lua>, F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result; + + // + // Below are internal methods used in generated code + // + + #[doc(hidden)] + fn add_callback(&mut self, _name: Vec, _callback: Callback<'lua, 'static>) {} + + #[doc(hidden)] + #[cfg(feature = "async")] + fn add_async_callback(&mut self, _name: Vec, _callback: AsyncCallback<'lua, 'static>) {} + + #[doc(hidden)] + fn add_meta_callback(&mut self, _meta: MetaMethod, _callback: Callback<'lua, 'static>) {} } /// Field registry for [`UserData`] implementors. @@ -474,6 +491,16 @@ pub trait UserDataFields<'lua, T: UserData> { S: Into, F: 'static + MaybeSend + Fn(&'lua Lua) -> Result, R: ToLua<'lua>; + + // + // Below are internal methods used in generated code + // + + #[doc(hidden)] + fn add_field_getter(&mut self, _name: Vec, _callback: Callback<'lua, 'static>) {} + + #[doc(hidden)] + fn add_field_setter(&mut self, _name: Vec, _callback: Callback<'lua, 'static>) {} } /// Trait for custom userdata types. @@ -550,26 +577,11 @@ pub trait UserData: Sized { } // Wraps UserData in a way to always implement `serde::Serialize` trait. -pub(crate) enum UserDataCell { - Arc(Arc>>), - Plain(RefCell>), -} +pub(crate) struct UserDataCell(RefCell>); impl UserDataCell { pub(crate) fn new(data: T) -> Self { - UserDataCell::Plain(RefCell::new(UserDataWrapped { - data: Box::into_raw(Box::new(data)), - #[cfg(feature = "serialize")] - ser: Box::into_raw(Box::new(UserDataSerializeError)), - })) - } - - pub(crate) fn new_arc(data: T) -> Self { - UserDataCell::Arc(Arc::new(RefCell::new(UserDataWrapped { - data: Box::into_raw(Box::new(data)), - #[cfg(feature = "serialize")] - ser: Box::into_raw(Box::new(UserDataSerializeError)), - }))) + UserDataCell(RefCell::new(UserDataWrapped::new(data))) } #[cfg(feature = "serialize")] @@ -577,40 +589,135 @@ impl UserDataCell { where T: 'static + Serialize, { - let data_raw = Box::into_raw(Box::new(data)); - UserDataCell::Plain(RefCell::new(UserDataWrapped { - data: data_raw, - ser: data_raw, - })) + UserDataCell(RefCell::new(UserDataWrapped::new_ser(data))) + } + + // Immutably borrows the wrapped value. + fn try_borrow(&self) -> Result> { + self.0 + .try_borrow() + .map(|r| UserDataRef(UserDataRefInner::Ref(r))) + .map_err(|_| Error::UserDataBorrowError) + } + + // Mutably borrows the wrapped value. + fn try_borrow_mut(&self) -> Result> { + self.0 + .try_borrow_mut() + .map(|r| UserDataRefMut(UserDataRefMutInner::Ref(r))) + .map_err(|_| Error::UserDataBorrowMutError) } } -impl Deref for UserDataCell { - type Target = RefCell>; +#[cfg(feature = "serialize")] +impl Serialize for UserDataCell<()> { + fn serialize(&self, serializer: S) -> StdResult + where + S: Serializer, + { + let ser = self + .0 + .try_borrow() + .map_err(|_| ser::Error::custom(Error::UserDataBorrowError))? + .ser; + unsafe { (&*ser).serialize(serializer) } + } +} + +/// A wrapper type for an immutably borrowed value from an `AnyUserData`. +pub struct UserDataRef<'a, T>(UserDataRefInner<'a, T>); + +enum UserDataRefInner<'a, T> { + Ref(Ref<'a, UserDataWrapped>), +} + +/// A wrapper type for a mutably borrowed value from an `AnyUserData`. +pub struct UserDataRefMut<'a, T>(UserDataRefMutInner<'a, T>); + +enum UserDataRefMutInner<'a, T> { + Ref(RefMut<'a, UserDataWrapped>), +} + +impl Deref for UserDataRef<'_, T> { + type Target = T; fn deref(&self) -> &Self::Target { - match self { - UserDataCell::Arc(t) => &*t, - UserDataCell::Plain(t) => &*t, + match &self.0 { + UserDataRefInner::Ref(x) => &*x, } } } -impl Clone for UserDataCell { - fn clone(&self) -> Self { - match self { - UserDataCell::Arc(t) => UserDataCell::Arc(t.clone()), - UserDataCell::Plain(_) => mlua_panic!("cannot clone non-arc userdata"), +impl Deref for UserDataRefMut<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match &self.0 { + UserDataRefMutInner::Ref(x) => &*x, } } } +impl DerefMut for UserDataRefMut<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match &mut self.0 { + UserDataRefMutInner::Ref(x) => &mut *x, + } + } +} + +impl fmt::Debug for UserDataRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&*self as &T, f) + } +} + +impl fmt::Debug for UserDataRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&*self as &T, f) + } +} + +impl fmt::Display for UserDataRef<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self as &T, f) + } +} + +impl fmt::Display for UserDataRefMut<'_, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(&*self as &T, f) + } +} + pub(crate) struct UserDataWrapped { pub(crate) data: *mut T, #[cfg(feature = "serialize")] ser: *mut dyn erased_serde::Serialize, } +impl UserDataWrapped { + fn new(data: T) -> Self { + UserDataWrapped { + data: Box::into_raw(Box::new(data)), + #[cfg(feature = "serialize")] + ser: Box::into_raw(Box::new(UserDataSerializeError)), + } + } + + #[cfg(feature = "serialize")] + fn new_ser(data: T) -> Self + where + T: 'static + Serialize, + { + let data_raw = Box::into_raw(Box::new(data)); + UserDataWrapped { + data: data_raw, + ser: data_raw, + } + } +} + impl Drop for UserDataWrapped { fn drop(&mut self) { unsafe { @@ -623,20 +730,22 @@ impl Drop for UserDataWrapped { } } -impl AsRef for UserDataWrapped { - fn as_ref(&self) -> &T { +impl Deref for UserDataWrapped { + type Target = T; + + fn deref(&self) -> &Self::Target { unsafe { &*self.data } } } -impl AsMut for UserDataWrapped { - fn as_mut(&mut self) -> &mut T { +impl DerefMut for UserDataWrapped { + fn deref_mut(&mut self) -> &mut Self::Target { unsafe { &mut *self.data } } } #[cfg(feature = "serialize")] -pub(crate) struct UserDataSerializeError; +struct UserDataSerializeError; #[cfg(feature = "serialize")] impl Serialize for UserDataSerializeError { @@ -683,26 +792,18 @@ impl<'lua> AnyUserData<'lua> { /// /// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a /// `UserDataTypeMismatch` if the userdata is not of type `T`. - pub fn borrow(&self) -> Result> { - self.inspect(|cell| { - let cell_ref = cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?; - Ok(Ref::map(cell_ref, |x| unsafe { &*x.data })) - }) + pub fn borrow(&self) -> Result> { + self.inspect(|cell| cell.try_borrow()) } /// Borrow this userdata mutably if it is of type `T`. /// /// # Errors /// - /// Returns a `UserDataBorrowMutError` if the userdata is already borrowed. Returns a - /// `UserDataTypeMismatch` if the userdata is not of type `T`. - pub fn borrow_mut(&self) -> Result> { - self.inspect(|cell| { - let cell_ref = cell - .try_borrow_mut() - .map_err(|_| Error::UserDataBorrowMutError)?; - Ok(RefMut::map(cell_ref, |x| unsafe { &mut *x.data })) - }) + /// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed. + /// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`. + pub fn borrow_mut(&self) -> Result> { + self.inspect(|cell| cell.try_borrow_mut()) } /// Sets an associated value to this `AnyUserData`. @@ -726,7 +827,7 @@ impl<'lua> AnyUserData<'lua> { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 3)?; - lua.push_userdata_ref(&self.0)?; + lua.push_userdata_ref(&self.0, false)?; lua.push_value(v)?; ffi::lua_setuservalue(lua.state, -2); @@ -745,7 +846,7 @@ impl<'lua> AnyUserData<'lua> { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 3)?; - lua.push_userdata_ref(&self.0)?; + lua.push_userdata_ref(&self.0, false)?; ffi::lua_getuservalue(lua.state, -1); lua.pop_value() }; @@ -776,7 +877,7 @@ impl<'lua> AnyUserData<'lua> { let _sg = StackGuard::new(lua.state); check_stack(lua.state, 3)?; - lua.push_userdata_ref(&self.0)?; + lua.push_userdata_ref(&self.0, false)?; ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call Ok(Table(lua.pop_ref())) } @@ -803,6 +904,25 @@ impl<'lua> AnyUserData<'lua> { Ok(false) } + pub(crate) fn type_id(&self) -> Result { + let lua = self.0.lua; + unsafe { + let _sg = StackGuard::new(lua.state); + check_stack(lua.state, 5)?; + + // Push userdata with metatable + lua.push_userdata_ref(&self.0, true)?; + + // Get the special `__mlua_type_id` + ffi::safe::lua_pushstring(lua.state, "__mlua_type_id")?; + if ffi::lua_rawget(lua.state, -2) != ffi::LUA_TUSERDATA { + return Err(Error::UserDataTypeMismatch); + } + + Ok(*(ffi::lua_touserdata(lua.state, -1) as *const TypeId)) + } + } + fn inspect<'a, T, R, F>(&'a self, func: F) -> Result where T: 'static + UserData, @@ -928,17 +1048,15 @@ impl<'lua> Serialize for AnyUserData<'lua> { where S: Serializer, { - let res = (|| unsafe { + unsafe { let lua = self.0.lua; let _sg = StackGuard::new(lua.state); - check_stack(lua.state, 3)?; + check_stack(lua.state, 3).map_err(ser::Error::custom)?; - lua.push_userdata_ref(&self.0)?; + lua.push_userdata_ref(&self.0, false) + .map_err(ser::Error::custom)?; let ud = &*get_userdata::>(lua.state, -1); - (*ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?.ser) - .serialize(serializer) - .map_err(|err| Error::SerializeError(err.to_string())) - })(); - res.map_err(ser::Error::custom) + ud.serialize(serializer) + } } } diff --git a/tests/userdata.rs b/tests/userdata.rs index 601fa91..e88e97b 100644 --- a/tests/userdata.rs +++ b/tests/userdata.rs @@ -1,4 +1,7 @@ -use std::sync::Arc; +use std::sync::{Arc, Mutex, RwLock}; + +#[cfg(not(feature = "send"))] +use std::{cell::RefCell, rc::Rc}; #[cfg(feature = "lua54")] use std::sync::atomic::{AtomicI64, Ordering}; @@ -451,3 +454,53 @@ fn test_metatable() -> Result<()> { Ok(()) } + +#[test] +fn test_userdata_wrapped() -> Result<()> { + struct MyUserData(i64); + + impl UserData for MyUserData { + fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_method_get("data", |_, this| Ok(this.0)); + fields.add_field_method_set("data", |_, this, val| { + this.0 = val; + Ok(()) + }) + } + } + + let lua = Lua::new(); + let globals = lua.globals(); + + #[cfg(not(feature = "send"))] + { + globals.set("rc_refcell_ud", Rc::new(RefCell::new(MyUserData(1))))?; + lua.load( + r#" + rc_refcell_ud.data = rc_refcell_ud.data + 1 + assert(rc_refcell_ud.data == 2) + "#, + ) + .exec()?; + } + + globals.set("arc_mutex_ud", Arc::new(Mutex::new(MyUserData(2))))?; + lua.load( + r#" + arc_mutex_ud.data = arc_mutex_ud.data + 1 + assert(arc_mutex_ud.data == 3) + "#, + ) + .exec()?; + + globals.set("arc_rwlock_ud", Arc::new(RwLock::new(MyUserData(3))))?; + lua.load( + r#" + arc_rwlock_ud.data = arc_rwlock_ud.data + 1 + assert(arc_rwlock_ud.data == 4) + "#, + ) + .exec()?; + + Ok(()) +}