From 4fe7d151a17a676c76a186c36b4b310c49bca5cc Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Fri, 21 Mar 2025 12:29:47 +0000 Subject: [PATCH] Refactor `userdata-wrappers` feature. Support borrowing underlying data in `UserDataRef` and `UserDataRefMut`. --- src/state.rs | 3 +- src/state/raw.rs | 21 +- src/userdata.rs | 55 +++-- src/userdata/cell.rs | 362 +++++------------------------- src/userdata/lock.rs | 39 ++++ src/userdata/ref.rs | 474 +++++++++++++++++++++++++++++++++++++++ src/userdata/registry.rs | 278 ++++------------------- src/util/mod.rs | 5 +- src/util/userdata.rs | 197 +++++++++++++++- tests/userdata.rs | 356 +++++++++++++++++++++++------ 10 files changed, 1146 insertions(+), 644 deletions(-) create mode 100644 src/userdata/ref.rs diff --git a/src/state.rs b/src/state.rs index fb0bda4..fbfb8db 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1330,7 +1330,7 @@ impl Lua { /// This methods provides a way to add fields or methods to userdata objects of a type `T`. pub fn register_userdata_type(&self, f: impl FnOnce(&mut UserDataRegistry)) -> Result<()> { let type_id = TypeId::of::(); - let mut registry = UserDataRegistry::new(self, type_id); + let mut registry = UserDataRegistry::new(self); f(&mut registry); let lua = self.lock(); @@ -1499,7 +1499,6 @@ impl Lua { &self, f: impl for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> Result, ) -> Result { - // TODO: Update to `&Scope` in next major release f(&Scope::new(self.lock_arc())) } diff --git a/src/state/raw.rs b/src/state/raw.rs index 1742069..e24ec87 100644 --- a/src/state/raw.rs +++ b/src/state/raw.rs @@ -864,7 +864,7 @@ impl RawLua { } // Create a new metatable from `UserData` definition - let mut registry = UserDataRegistry::new(self.lua(), type_id); + let mut registry = UserDataRegistry::new(self.lua()); T::register(&mut registry); self.create_userdata_metatable(registry.into_raw()) @@ -885,7 +885,7 @@ impl RawLua { // Check if metatable creation is pending or create an empty metatable otherwise let registry = match (*self.extra.get()).pending_userdata_reg.remove(&type_id) { Some(registry) => registry, - None => UserDataRegistry::::new(self.lua(), type_id).into_raw(), + None => UserDataRegistry::::new(self.lua()).into_raw(), }; self.create_userdata_metatable(registry) }) @@ -1103,17 +1103,22 @@ impl RawLua { // Returns `TypeId` for the userdata ref, checking that it's registered and not destructed. // // Returns `None` if the userdata is registered but non-static. - pub(crate) unsafe fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result> { - self.get_userdata_type_id_inner(self.ref_thread(), vref.index) + #[inline(always)] + pub(crate) fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result> { + unsafe { self.get_userdata_type_id_inner(self.ref_thread(), vref.index) } } // Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack. - pub(crate) unsafe fn get_userdata_type_id(&self, idx: c_int) -> Result> { - match self.get_userdata_type_id_inner(self.state(), idx) { + pub(crate) unsafe fn get_userdata_type_id( + &self, + state: *mut ffi::lua_State, + idx: c_int, + ) -> Result> { + match self.get_userdata_type_id_inner(state, idx) { Ok(type_id) => Ok(type_id), - Err(Error::UserDataTypeMismatch) if ffi::lua_type(self.state(), idx) != ffi::LUA_TUSERDATA => { + Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => { // Report `FromLuaConversionError` instead - let idx_type_name = CStr::from_ptr(ffi::luaL_typename(self.state(), idx)); + let idx_type_name = CStr::from_ptr(ffi::luaL_typename(state, idx)); let idx_type_name = idx_type_name.to_str().unwrap(); let message = format!("expected userdata of type '{}'", short_type_name::()); Err(Error::from_lua_conversion(idx_type_name, "userdata", message)) diff --git a/src/userdata.rs b/src/userdata.rs index 83c5efc..86cdbc6 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -12,7 +12,10 @@ use crate::string::String; use crate::table::{Table, TablePairs}; use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti}; use crate::types::{MaybeSend, ValueRef}; -use crate::util::{check_stack, get_userdata, push_string, take_userdata, StackGuard}; +use crate::util::{ + borrow_userdata_scoped, borrow_userdata_scoped_mut, check_stack, get_userdata, push_string, + take_userdata, StackGuard, TypeIdHints, +}; use crate::value::Value; #[cfg(feature = "async")] @@ -26,7 +29,7 @@ use { // Re-export for convenience pub(crate) use cell::UserDataStorage; -pub use cell::{UserDataRef, UserDataRefMut}; +pub use r#ref::{UserDataRef, UserDataRefMut}; pub use registry::UserDataRegistry; pub(crate) use registry::{RawUserDataRegistry, UserDataProxy}; @@ -622,7 +625,10 @@ impl AnyUserData { /// Checks whether the type of this userdata is `T`. #[inline] pub fn is(&self) -> bool { - self.inspect::(|_| Ok(())).is_ok() + let lua = self.0.lua.lock(); + let type_id = lua.get_userdata_ref_type_id(&self.0); + // We do not use wrapped types here, rather prefer to check the "real" type of the userdata + matches!(type_id, Ok(Some(type_id)) if type_id == TypeId::of::()) } /// Borrow this userdata immutably if it is of type `T`. @@ -637,7 +643,8 @@ impl AnyUserData { /// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch #[inline] pub fn borrow(&self) -> Result> { - self.inspect(|ud| ud.try_borrow_owned()) + let lua = self.0.lua.lock(); + unsafe { UserDataRef::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) } } /// Borrow this userdata immutably if it is of type `T`, passing the borrowed value @@ -645,7 +652,10 @@ impl AnyUserData { /// /// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]). pub fn borrow_scoped(&self, f: impl FnOnce(&T) -> R) -> Result { - self.inspect(|ud| ud.try_borrow_scoped(|ud| f(ud))) + let lua = self.0.lua.lock(); + let type_id = lua.get_userdata_ref_type_id(&self.0)?; + let type_hints = TypeIdHints::new::(); + unsafe { borrow_userdata_scoped(lua.ref_thread(), self.0.index, type_id, type_hints, f) } } /// Borrow this userdata mutably if it is of type `T`. @@ -660,7 +670,8 @@ impl AnyUserData { /// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch #[inline] pub fn borrow_mut(&self) -> Result> { - self.inspect(|ud| ud.try_borrow_owned_mut()) + let lua = self.0.lua.lock(); + unsafe { UserDataRefMut::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) } } /// Borrow this userdata mutably if it is of type `T`, passing the borrowed value @@ -668,7 +679,10 @@ impl AnyUserData { /// /// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]). pub fn borrow_mut_scoped(&self, f: impl FnOnce(&mut T) -> R) -> Result { - self.inspect(|ud| ud.try_borrow_scoped_mut(|ud| f(ud))) + let lua = self.0.lua.lock(); + let type_id = lua.get_userdata_ref_type_id(&self.0)?; + let type_hints = TypeIdHints::new::(); + unsafe { borrow_userdata_scoped_mut(lua.ref_thread(), self.0.index, type_id, type_hints, f) } } /// Takes the value out of this userdata. @@ -687,9 +701,11 @@ impl AnyUserData { let type_id = lua.push_userdata_ref(&self.0)?; match type_id { Some(type_id) if type_id == TypeId::of::() => { - // Try to borrow userdata exclusively - let _ = (*get_userdata::>(state, -1)).try_borrow_mut()?; - take_userdata::>(state).into_inner() + if (*get_userdata::>(state, -1)).has_exclusive_access() { + take_userdata::>(state).into_inner() + } else { + Err(Error::UserDataBorrowMutError) + } } _ => Err(Error::UserDataTypeMismatch), } @@ -965,24 +981,6 @@ impl AnyUserData { }; is_serializable().unwrap_or(false) } - - pub(crate) fn inspect(&self, func: F) -> Result - where - T: 'static, - F: FnOnce(&UserDataStorage) -> Result, - { - let lua = self.0.lua.lock(); - unsafe { - let type_id = lua.get_userdata_ref_type_id(&self.0)?; - match type_id { - Some(type_id) if type_id == TypeId::of::() => { - let ud = get_userdata::>(lua.ref_thread(), self.0.index); - func(&*ud) - } - _ => Err(Error::UserDataTypeMismatch), - } - } - } } /// Handle to a [`AnyUserData`] metatable. @@ -1106,6 +1104,7 @@ where mod cell; mod lock; mod object; +mod r#ref; mod registry; mod util; diff --git a/src/userdata/cell.rs b/src/userdata/cell.rs index 70b6dd3..538e33d 100644 --- a/src/userdata/cell.rs +++ b/src/userdata/cell.rs @@ -1,22 +1,13 @@ -use std::any::{type_name, TypeId}; use std::cell::{RefCell, UnsafeCell}; -use std::fmt; -use std::ops::{Deref, DerefMut}; -use std::os::raw::c_int; #[cfg(feature = "serialize")] use serde::ser::{Serialize, Serializer}; use crate::error::{Error, Result}; -use crate::state::{Lua, RawLua}; -use crate::traits::FromLua; use crate::types::XRc; -use crate::userdata::AnyUserData; -use crate::util::get_userdata; -use crate::value::Value; use super::lock::{RawLock, UserDataLock}; -use super::util::is_sync; +use super::r#ref::{UserDataRef, UserDataRefMut}; #[cfg(all(feature = "serialize", not(feature = "send")))] type DynSerialize = dyn erased_serde::Serialize; @@ -34,7 +25,7 @@ pub(crate) enum UserDataStorage { pub(crate) enum UserDataVariant { Default(XRc>), #[cfg(feature = "serialize")] - Serializable(XRc>>), + Serializable(XRc>>, bool), // bool is `is_sync` } impl Clone for UserDataVariant { @@ -43,16 +34,28 @@ impl Clone for UserDataVariant { match self { Self::Default(inner) => Self::Default(XRc::clone(inner)), #[cfg(feature = "serialize")] - Self::Serializable(inner) => Self::Serializable(XRc::clone(inner)), + Self::Serializable(inner, is_sync) => Self::Serializable(XRc::clone(inner), *is_sync), } } } impl UserDataVariant { - // Immutably borrows the wrapped value in-place. #[inline(always)] - fn try_borrow(&self) -> Result> { - UserDataBorrowRef::try_from(self) + pub(super) fn try_borrow_scoped(&self, f: impl FnOnce(&T) -> R) -> Result { + // We don't need to check for `T: Sync` because when this method is used (internally), + // Lua mutex is already locked. + // If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be + // exclusively locked. + let _guard = (self.raw_lock().try_lock_shared_guarded()).map_err(|_| Error::UserDataBorrowError)?; + Ok(f(unsafe { &*self.as_ptr() })) + } + + // Mutably borrows the wrapped value in-place. + #[inline(always)] + fn try_borrow_scoped_mut(&self, f: impl FnOnce(&mut T) -> R) -> Result { + let _guard = + (self.raw_lock().try_lock_exclusive_guarded()).map_err(|_| Error::UserDataBorrowMutError)?; + Ok(f(unsafe { &mut *self.as_ptr() })) } // Immutably borrows the wrapped value and returns an owned reference. @@ -61,12 +64,6 @@ impl UserDataVariant { UserDataRef::try_from(self.clone()) } - // Mutably borrows the wrapped value in-place. - #[inline(always)] - fn try_borrow_mut(&self) -> Result> { - UserDataBorrowMut::try_from(self) - } - // Mutably borrows the wrapped value and returns an owned reference. #[inline(always)] fn try_borrow_owned_mut(&self) -> Result> { @@ -83,7 +80,7 @@ impl UserDataVariant { Ok(match self { Self::Default(inner) => XRc::into_inner(inner).unwrap().value.into_inner(), #[cfg(feature = "serialize")] - Self::Serializable(inner) => unsafe { + Self::Serializable(inner, _) => unsafe { let raw = Box::into_raw(XRc::into_inner(inner).unwrap().value.into_inner()); *Box::from_raw(raw as *mut T) }, @@ -95,25 +92,25 @@ impl UserDataVariant { match self { Self::Default(inner) => XRc::strong_count(inner), #[cfg(feature = "serialize")] - Self::Serializable(inner) => XRc::strong_count(inner), + Self::Serializable(inner, _) => XRc::strong_count(inner), } } #[inline(always)] - fn raw_lock(&self) -> &RawLock { + pub(super) fn raw_lock(&self) -> &RawLock { match self { Self::Default(inner) => &inner.raw_lock, #[cfg(feature = "serialize")] - Self::Serializable(inner) => &inner.raw_lock, + Self::Serializable(inner, _) => &inner.raw_lock, } } #[inline(always)] - fn as_ptr(&self) -> *mut T { + pub(super) fn as_ptr(&self) -> *mut T { match self { Self::Default(inner) => inner.value.get(), #[cfg(feature = "serialize")] - Self::Serializable(inner) => unsafe { &mut **(inner.value.get() as *mut Box) }, + Self::Serializable(inner, _) => unsafe { &mut **(inner.value.get() as *mut Box) }, } } } @@ -122,14 +119,24 @@ impl UserDataVariant { impl Serialize for UserDataStorage<()> { fn serialize(&self, serializer: S) -> std::result::Result { match self { - Self::Owned(UserDataVariant::Serializable(inner)) => unsafe { - // We need to borrow the inner value exclusively to serialize it. + Self::Owned(variant @ UserDataVariant::Serializable(inner, is_sync)) => unsafe { #[cfg(feature = "send")] - let _guard = self.try_borrow_mut().map_err(serde::ser::Error::custom)?; - // No need to do this if the `send` feature is disabled. + if *is_sync { + let _guard = (variant.raw_lock().try_lock_shared_guarded()) + .map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?; + (*inner.value.get()).serialize(serializer) + } else { + let _guard = (variant.raw_lock().try_lock_exclusive_guarded()) + .map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?; + (*inner.value.get()).serialize(serializer) + } #[cfg(not(feature = "send"))] - let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?; - (*inner.value.get()).serialize(serializer) + { + let _ = is_sync; + let _guard = (variant.raw_lock().try_lock_shared_guarded()) + .map_err(|_| serde::ser::Error::custom(Error::UserDataBorrowError))?; + (*inner.value.get()).serialize(serializer) + } }, _ => Err(serde::ser::Error::custom("cannot serialize ")), } @@ -157,232 +164,6 @@ impl UserDataCell { } } -/// A wrapper type for a userdata value that provides read access. -/// -/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua. -pub struct UserDataRef(UserDataVariant); - -impl Deref for UserDataRef { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - unsafe { &*self.0.as_ptr() } - } -} - -impl Drop for UserDataRef { - #[inline] - fn drop(&mut self) { - if !cfg!(feature = "send") || is_sync::() { - unsafe { self.0.raw_lock().unlock_shared() }; - } else { - unsafe { self.0.raw_lock().unlock_exclusive() }; - } - } -} - -impl fmt::Debug for UserDataRef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl fmt::Display for UserDataRef { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl TryFrom> for UserDataRef { - type Error = Error; - - #[inline] - fn try_from(variant: UserDataVariant) -> Result { - if !cfg!(feature = "send") || is_sync::() { - if !variant.raw_lock().try_lock_shared() { - return Err(Error::UserDataBorrowError); - } - } else if !variant.raw_lock().try_lock_exclusive() { - return Err(Error::UserDataBorrowError); - } - Ok(UserDataRef(variant)) - } -} - -impl FromLua for UserDataRef { - fn from_lua(value: Value, _: &Lua) -> Result { - try_value_to_userdata::(value)?.borrow() - } - - unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result { - let type_id = lua.get_userdata_type_id::(idx)?; - match type_id { - Some(type_id) if type_id == TypeId::of::() => { - (*get_userdata::>(lua.state(), idx)).try_borrow_owned() - } - _ => Err(Error::UserDataTypeMismatch), - } - } -} - -/// A wrapper type for a userdata value that provides read and write access. -/// -/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua. -pub struct UserDataRefMut(UserDataVariant); - -impl Deref for UserDataRefMut { - type Target = T; - - #[inline] - fn deref(&self) -> &Self::Target { - unsafe { &*self.0.as_ptr() } - } -} - -impl DerefMut for UserDataRefMut { - #[inline] - fn deref_mut(&mut self) -> &mut Self::Target { - unsafe { &mut *self.0.as_ptr() } - } -} - -impl Drop for UserDataRefMut { - #[inline] - fn drop(&mut self) { - unsafe { self.0.raw_lock().unlock_exclusive() }; - } -} - -impl fmt::Debug for UserDataRefMut { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl fmt::Display for UserDataRefMut { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - (**self).fmt(f) - } -} - -impl TryFrom> for UserDataRefMut { - type Error = Error; - - #[inline] - fn try_from(variant: UserDataVariant) -> Result { - if !variant.raw_lock().try_lock_exclusive() { - return Err(Error::UserDataBorrowMutError); - } - Ok(UserDataRefMut(variant)) - } -} - -impl FromLua for UserDataRefMut { - fn from_lua(value: Value, _: &Lua) -> Result { - try_value_to_userdata::(value)?.borrow_mut() - } - - unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result { - let type_id = lua.get_userdata_type_id::(idx)?; - match type_id { - Some(type_id) if type_id == TypeId::of::() => { - (*get_userdata::>(lua.state(), idx)).try_borrow_owned_mut() - } - _ => Err(Error::UserDataTypeMismatch), - } - } -} - -/// A type that provides read access to a userdata value (borrowing the value). -pub(crate) struct UserDataBorrowRef<'a, T>(&'a UserDataVariant); - -impl Drop for UserDataBorrowRef<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.0.raw_lock().unlock_shared(); - } - } -} - -impl Deref for UserDataBorrowRef<'_, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - // SAFETY: `UserDataBorrowRef` is only created with shared access to the value. - unsafe { &*self.0.as_ptr() } - } -} - -impl<'a, T> TryFrom<&'a UserDataVariant> for UserDataBorrowRef<'a, T> { - type Error = Error; - - #[inline(always)] - fn try_from(variant: &'a UserDataVariant) -> Result { - // We don't need to check for `T: Sync` because when this method is used (internally), - // Lua mutex is already locked. - // If non-`Sync` userdata is already borrowed by another thread (via `UserDataRef`), it will be - // exclusively locked. - if !variant.raw_lock().try_lock_shared() { - return Err(Error::UserDataBorrowError); - } - Ok(UserDataBorrowRef(variant)) - } -} - -pub(crate) struct UserDataBorrowMut<'a, T>(&'a UserDataVariant); - -impl Drop for UserDataBorrowMut<'_, T> { - #[inline] - fn drop(&mut self) { - unsafe { - self.0.raw_lock().unlock_exclusive(); - } - } -} - -impl Deref for UserDataBorrowMut<'_, T> { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - unsafe { &*self.0.as_ptr() } - } -} - -impl DerefMut for UserDataBorrowMut<'_, T> { - #[inline] - fn deref_mut(&mut self) -> &mut T { - unsafe { &mut *self.0.as_ptr() } - } -} - -impl<'a, T> TryFrom<&'a UserDataVariant> for UserDataBorrowMut<'a, T> { - type Error = Error; - - #[inline(always)] - fn try_from(variant: &'a UserDataVariant) -> Result { - if !variant.raw_lock().try_lock_exclusive() { - return Err(Error::UserDataBorrowMutError); - } - Ok(UserDataBorrowMut(variant)) - } -} - -#[inline] -fn try_value_to_userdata(value: Value) -> Result { - match value { - Value::UserData(ud) => Ok(ud), - _ => Err(Error::FromLuaConversionError { - from: value.type_name(), - to: "userdata".to_string(), - message: Some(format!("expected userdata of type {}", type_name::())), - }), - } -} - pub(crate) enum ScopedUserDataVariant { Ref(*const T), RefMut(RefCell<*mut T>), @@ -423,13 +204,15 @@ impl UserDataStorage { T: Serialize + crate::types::MaybeSend, { let data = Box::new(data) as Box; - Self::Owned(UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)))) + let is_sync = super::util::is_sync::(); + let variant = UserDataVariant::Serializable(XRc::new(UserDataCell::new(data)), is_sync); + Self::Owned(variant) } #[cfg(feature = "serialize")] #[inline(always)] pub(crate) fn is_serializable(&self) -> bool { - matches!(self, Self::Owned(UserDataVariant::Serializable(_))) + matches!(self, Self::Owned(UserDataVariant::Serializable(..))) } // Immutably borrows the wrapped value and returns an owned reference. @@ -441,23 +224,6 @@ impl UserDataStorage { } } - #[allow(unused)] - #[inline(always)] - pub(crate) fn try_borrow(&self) -> Result> { - match self { - Self::Owned(data) => data.try_borrow(), - Self::Scoped(_) => Err(Error::UserDataTypeMismatch), - } - } - - #[inline(always)] - pub(crate) fn try_borrow_mut(&self) -> Result> { - match self { - Self::Owned(data) => data.try_borrow_mut(), - Self::Scoped(_) => Err(Error::UserDataTypeMismatch), - } - } - // Mutably borrows the wrapped value and returns an owned reference. #[inline(always)] pub(crate) fn try_borrow_owned_mut(&self) -> Result> { @@ -495,10 +261,19 @@ impl UserDataStorage { } } + /// Returns `true` if the container has exclusive access to the value. + #[inline(always)] + pub(crate) fn has_exclusive_access(&self) -> bool { + match self { + Self::Owned(variant) => !variant.raw_lock().is_locked(), + Self::Scoped(_) => false, + } + } + #[inline] pub(crate) fn try_borrow_scoped(&self, f: impl FnOnce(&T) -> R) -> Result { match self { - Self::Owned(data) => Ok(f(&*data.try_borrow()?)), + Self::Owned(data) => data.try_borrow_scoped(f), Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })), Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => { let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?; @@ -510,7 +285,7 @@ impl UserDataStorage { #[inline] pub(crate) fn try_borrow_scoped_mut(&self, f: impl FnOnce(&mut T) -> R) -> Result { match self { - Self::Owned(data) => Ok(f(&mut *data.try_borrow_mut()?)), + Self::Owned(data) => data.try_borrow_scoped_mut(f), Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError), Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => { let mut t = value @@ -521,30 +296,3 @@ impl UserDataStorage { } } } - -#[cfg(test)] -mod assertions { - use super::*; - - #[cfg(feature = "send")] - static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync); - #[cfg(feature = "send")] - static_assertions::assert_not_impl_all!(UserDataRef>: Send, Sync); - #[cfg(feature = "send")] - static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send); - #[cfg(feature = "send")] - static_assertions::assert_not_impl_all!(UserDataRefMut>: Send, Sync); - #[cfg(feature = "send")] - static_assertions::assert_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync); - #[cfg(feature = "send")] - static_assertions::assert_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync); - - #[cfg(not(feature = "send"))] - static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync); - #[cfg(not(feature = "send"))] - static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync); - #[cfg(not(feature = "send"))] - static_assertions::assert_not_impl_all!(UserDataBorrowRef<'_, ()>: Send, Sync); - #[cfg(not(feature = "send"))] - static_assertions::assert_not_impl_all!(UserDataBorrowMut<'_, ()>: Send, Sync); -} diff --git a/src/userdata/lock.rs b/src/userdata/lock.rs index 4843ff4..e0e5d1a 100644 --- a/src/userdata/lock.rs +++ b/src/userdata/lock.rs @@ -7,6 +7,45 @@ pub(crate) trait UserDataLock { unsafe fn unlock_shared(&self); unsafe fn unlock_exclusive(&self); + + fn try_lock_shared_guarded(&self) -> Result, ()> { + if self.try_lock_shared() { + Ok(LockGuard { + lock: self, + exclusive: false, + }) + } else { + Err(()) + } + } + + fn try_lock_exclusive_guarded(&self) -> Result, ()> { + if self.try_lock_exclusive() { + Ok(LockGuard { + lock: self, + exclusive: true, + }) + } else { + Err(()) + } + } +} + +pub(crate) struct LockGuard<'a, L: UserDataLock + ?Sized> { + lock: &'a L, + exclusive: bool, +} + +impl Drop for LockGuard<'_, L> { + fn drop(&mut self) { + unsafe { + if self.exclusive { + self.lock.unlock_exclusive(); + } else { + self.lock.unlock_shared(); + } + } + } } pub(crate) use lock_impl::RawLock; diff --git a/src/userdata/ref.rs b/src/userdata/ref.rs new file mode 100644 index 0000000..750443a --- /dev/null +++ b/src/userdata/ref.rs @@ -0,0 +1,474 @@ +use std::any::{type_name, TypeId}; +use std::ops::{Deref, DerefMut}; +use std::os::raw::c_int; +use std::{fmt, mem}; + +use crate::error::{Error, Result}; +use crate::state::{Lua, RawLua}; +use crate::traits::FromLua; +use crate::userdata::AnyUserData; +use crate::util::get_userdata; +use crate::value::Value; + +use super::cell::{UserDataStorage, UserDataVariant}; +use super::lock::{LockGuard, RawLock, UserDataLock}; +use super::util::is_sync; + +#[cfg(feature = "userdata-wrappers")] +use { + parking_lot::{ + Mutex as MutexPL, MutexGuard as MutexGuardPL, RwLock as RwLockPL, + RwLockReadGuard as RwLockReadGuardPL, RwLockWriteGuard as RwLockWriteGuardPL, + }, + std::sync::Arc, +}; +#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] +use { + std::cell::{Ref, RefCell, RefMut}, + std::rc::Rc, +}; + +/// A wrapper type for a userdata value that provides read access. +/// +/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua. +pub struct UserDataRef { + // It's important to drop the guard first, as it refers to the `inner` data. + _guard: LockGuard<'static, RawLock>, + inner: UserDataRefInner, +} + +impl Deref for UserDataRef { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.inner + } +} + +impl fmt::Debug for UserDataRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for UserDataRef { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl TryFrom> for UserDataRef { + type Error = Error; + + #[inline] + fn try_from(variant: UserDataVariant) -> Result { + let guard = if !cfg!(feature = "send") || is_sync::() { + variant.raw_lock().try_lock_shared_guarded() + } else { + variant.raw_lock().try_lock_exclusive_guarded() + }; + let guard = guard.map_err(|_| Error::UserDataBorrowError)?; + let guard = unsafe { mem::transmute::, LockGuard<'static, _>>(guard) }; + Ok(UserDataRef::from_parts(UserDataRefInner::Default(variant), guard)) + } +} + +impl FromLua for UserDataRef { + fn from_lua(value: Value, _: &Lua) -> Result { + try_value_to_userdata::(value)?.borrow() + } + + #[inline] + unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result { + Self::borrow_from_stack(lua, lua.state(), idx) + } +} + +impl UserDataRef { + #[inline(always)] + fn from_parts(inner: UserDataRefInner, guard: LockGuard<'static, RawLock>) -> Self { + Self { _guard: guard, inner } + } + + #[cfg(feature = "userdata-wrappers")] + fn remap( + self, + f: impl FnOnce(UserDataVariant) -> Result>, + ) -> Result> { + match &self.inner { + UserDataRefInner::Default(variant) => { + let inner = f(variant.clone())?; + Ok(UserDataRef::from_parts(inner, self._guard)) + } + _ => Err(Error::UserDataTypeMismatch), + } + } + + pub(crate) unsafe fn borrow_from_stack( + lua: &RawLua, + state: *mut ffi::lua_State, + idx: c_int, + ) -> Result { + let type_id = lua.get_userdata_type_id::(state, idx)?; + match type_id { + Some(type_id) if type_id == TypeId::of::() => { + let ud = get_userdata::>(state, idx); + (*ud).try_borrow_owned() + } + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == TypeId::of::>() => { + let ud = get_userdata::>>(state, idx); + ((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc()) + } + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == TypeId::of::>>() => { + let ud = get_userdata::>>>(state, idx); + ((*ud).try_borrow_owned()).and_then(|ud| ud.transform_rc_refcell()) + } + + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == TypeId::of::>() => { + let ud = get_userdata::>>(state, idx); + ((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc()) + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == TypeId::of::>>() => { + let ud = get_userdata::>>>(state, idx); + ((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_mutex_pl()) + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == TypeId::of::>>() => { + let ud = get_userdata::>>>(state, idx); + ((*ud).try_borrow_owned()).and_then(|ud| ud.transform_arc_rwlock_pl()) + } + _ => Err(Error::UserDataTypeMismatch), + } + } +} + +#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] +impl UserDataRef> { + fn transform_rc(self) -> Result> { + self.remap(|variant| Ok(UserDataRefInner::Rc(variant))) + } +} + +#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] +impl UserDataRef>> { + fn transform_rc_refcell(self) -> Result> { + self.remap(|variant| unsafe { + let obj = &*variant.as_ptr(); + let r#ref = obj.try_borrow().map_err(|_| Error::UserDataBorrowError)?; + let borrow = std::mem::transmute::, Ref<'static, T>>(r#ref); + Ok(UserDataRefInner::RcRefCell(borrow, variant)) + }) + } +} + +#[cfg(feature = "userdata-wrappers")] +impl UserDataRef> { + fn transform_arc(self) -> Result> { + self.remap(|variant| Ok(UserDataRefInner::Arc(variant))) + } +} + +#[cfg(feature = "userdata-wrappers")] +impl UserDataRef>> { + fn transform_arc_mutex_pl(self) -> Result> { + self.remap(|variant| unsafe { + let obj = &*variant.as_ptr(); + let guard = obj.try_lock().ok_or(Error::UserDataBorrowError)?; + let borrow = std::mem::transmute::, MutexGuardPL<'static, T>>(guard); + Ok(UserDataRefInner::ArcMutexPL(borrow, variant)) + }) + } +} + +#[cfg(feature = "userdata-wrappers")] +impl UserDataRef>> { + fn transform_arc_rwlock_pl(self) -> Result> { + self.remap(|variant| unsafe { + let obj = &*variant.as_ptr(); + let guard = obj.try_read().ok_or(Error::UserDataBorrowError)?; + let borrow = std::mem::transmute::, RwLockReadGuardPL<'static, T>>(guard); + Ok(UserDataRefInner::ArcRwLockPL(borrow, variant)) + }) + } +} + +#[allow(unused)] +enum UserDataRefInner { + Default(UserDataVariant), + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Rc(UserDataVariant>), + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + RcRefCell(Ref<'static, T>, UserDataVariant>>), + + #[cfg(feature = "userdata-wrappers")] + Arc(UserDataVariant>), + #[cfg(feature = "userdata-wrappers")] + ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant>>), + #[cfg(feature = "userdata-wrappers")] + ArcRwLockPL(RwLockReadGuardPL<'static, T>, UserDataVariant>>), +} + +impl Deref for UserDataRefInner { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + match self { + Self::Default(inner) => unsafe { &*inner.as_ptr() }, + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Self::Rc(inner) => unsafe { &*Rc::as_ptr(&*inner.as_ptr()) }, + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Self::RcRefCell(x, ..) => x, + + #[cfg(feature = "userdata-wrappers")] + Self::Arc(inner) => unsafe { &*Arc::as_ptr(&*inner.as_ptr()) }, + #[cfg(feature = "userdata-wrappers")] + Self::ArcMutexPL(x, ..) => x, + #[cfg(feature = "userdata-wrappers")] + Self::ArcRwLockPL(x, ..) => x, + } + } +} + +/// A wrapper type for a userdata value that provides read and write access. +/// +/// It implements [`FromLua`] and can be used to receive a typed userdata from Lua. +pub struct UserDataRefMut { + // It's important to drop the guard first, as it refers to the `inner` data. + _guard: LockGuard<'static, RawLock>, + inner: UserDataRefMutInner, +} + +impl Deref for UserDataRefMut { + type Target = T; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl DerefMut for UserDataRefMut { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } +} + +impl fmt::Debug for UserDataRefMut { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Display for UserDataRefMut { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl TryFrom> for UserDataRefMut { + type Error = Error; + + #[inline] + fn try_from(variant: UserDataVariant) -> Result { + let guard = variant.raw_lock().try_lock_exclusive_guarded(); + let guard = guard.map_err(|_| Error::UserDataBorrowMutError)?; + let guard = unsafe { mem::transmute::, LockGuard<'static, _>>(guard) }; + Ok(UserDataRefMut::from_parts( + UserDataRefMutInner::Default(variant), + guard, + )) + } +} + +impl FromLua for UserDataRefMut { + fn from_lua(value: Value, _: &Lua) -> Result { + try_value_to_userdata::(value)?.borrow_mut() + } + + unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result { + Self::borrow_from_stack(lua, lua.state(), idx) + } +} + +impl UserDataRefMut { + #[inline(always)] + fn from_parts(inner: UserDataRefMutInner, guard: LockGuard<'static, RawLock>) -> Self { + Self { _guard: guard, inner } + } + + #[cfg(feature = "userdata-wrappers")] + fn remap( + self, + f: impl FnOnce(UserDataVariant) -> Result>, + ) -> Result> { + match &self.inner { + UserDataRefMutInner::Default(variant) => { + let inner = f(variant.clone())?; + Ok(UserDataRefMut::from_parts(inner, self._guard)) + } + _ => Err(Error::UserDataTypeMismatch), + } + } + + pub(crate) unsafe fn borrow_from_stack( + lua: &RawLua, + state: *mut ffi::lua_State, + idx: c_int, + ) -> Result { + let type_id = lua.get_userdata_type_id::(state, idx)?; + match type_id { + Some(type_id) if type_id == TypeId::of::() => { + let ud = get_userdata::>(state, idx); + (*ud).try_borrow_owned_mut() + } + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == TypeId::of::>() => Err(Error::UserDataBorrowMutError), + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == TypeId::of::>>() => { + let ud = get_userdata::>>>(state, idx); + ((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_rc_refcell()) + } + + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == TypeId::of::>() => Err(Error::UserDataBorrowMutError), + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == TypeId::of::>>() => { + let ud = get_userdata::>>>(state, idx); + ((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_mutex_pl()) + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == TypeId::of::>>() => { + let ud = get_userdata::>>>(state, idx); + ((*ud).try_borrow_owned_mut()).and_then(|ud| ud.transform_arc_rwlock_pl()) + } + _ => Err(Error::UserDataTypeMismatch), + } + } +} + +#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] +impl UserDataRefMut>> { + fn transform_rc_refcell(self) -> Result> { + self.remap(|variant| unsafe { + let obj = &*variant.as_ptr(); + let refmut = obj.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?; + let borrow = std::mem::transmute::, RefMut<'static, T>>(refmut); + Ok(UserDataRefMutInner::RcRefCell(borrow, variant)) + }) + } +} + +#[cfg(feature = "userdata-wrappers")] +impl UserDataRefMut>> { + fn transform_arc_mutex_pl(self) -> Result> { + self.remap(|variant| unsafe { + let obj = &*variant.as_ptr(); + let guard = obj.try_lock().ok_or(Error::UserDataBorrowMutError)?; + let borrow = std::mem::transmute::, MutexGuardPL<'static, T>>(guard); + Ok(UserDataRefMutInner::ArcMutexPL(borrow, variant)) + }) + } +} + +#[cfg(feature = "userdata-wrappers")] +impl UserDataRefMut>> { + fn transform_arc_rwlock_pl(self) -> Result> { + self.remap(|variant| unsafe { + let obj = &*variant.as_ptr(); + let guard = obj.try_write().ok_or(Error::UserDataBorrowMutError)?; + let borrow = std::mem::transmute::, RwLockWriteGuardPL<'static, T>>(guard); + Ok(UserDataRefMutInner::ArcRwLockPL(borrow, variant)) + }) + } +} + +#[allow(unused)] +enum UserDataRefMutInner { + Default(UserDataVariant), + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + RcRefCell(RefMut<'static, T>, UserDataVariant>>), + + #[cfg(feature = "userdata-wrappers")] + ArcMutexPL(MutexGuardPL<'static, T>, UserDataVariant>>), + #[cfg(feature = "userdata-wrappers")] + ArcRwLockPL(RwLockWriteGuardPL<'static, T>, UserDataVariant>>), +} + +impl Deref for UserDataRefMutInner { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + match self { + Self::Default(inner) => unsafe { &*inner.as_ptr() }, + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Self::RcRefCell(x, ..) => x, + + #[cfg(feature = "userdata-wrappers")] + Self::ArcMutexPL(x, ..) => x, + #[cfg(feature = "userdata-wrappers")] + Self::ArcRwLockPL(x, ..) => x, + } + } +} + +impl DerefMut for UserDataRefMutInner { + #[inline] + fn deref_mut(&mut self) -> &mut T { + match self { + Self::Default(inner) => unsafe { &mut *inner.as_ptr() }, + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Self::RcRefCell(x, ..) => x, + + #[cfg(feature = "userdata-wrappers")] + Self::ArcMutexPL(x, ..) => x, + #[cfg(feature = "userdata-wrappers")] + Self::ArcRwLockPL(x, ..) => x, + } + } +} + +#[inline] +fn try_value_to_userdata(value: Value) -> Result { + match value { + Value::UserData(ud) => Ok(ud), + _ => Err(Error::FromLuaConversionError { + from: value.type_name(), + to: "userdata".to_string(), + message: Some(format!("expected userdata of type {}", type_name::())), + }), + } +} + +#[cfg(test)] +mod assertions { + use super::*; + + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(UserDataRef<()>: Send, Sync); + #[cfg(feature = "send")] + static_assertions::assert_not_impl_all!(UserDataRef>: Send, Sync); + #[cfg(feature = "send")] + static_assertions::assert_impl_all!(UserDataRefMut<()>: Sync, Send); + #[cfg(feature = "send")] + static_assertions::assert_not_impl_all!(UserDataRefMut>: Send, Sync); + + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_all!(UserDataRef<()>: Send, Sync); + #[cfg(not(feature = "send"))] + static_assertions::assert_not_impl_all!(UserDataRefMut<()>: Send, Sync); +} diff --git a/src/userdata/registry.rs b/src/userdata/registry.rs index ec5a989..f9e88c9 100644 --- a/src/userdata/registry.rs +++ b/src/userdata/registry.rs @@ -11,7 +11,9 @@ use crate::state::{Lua, LuaGuard}; use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti}; use crate::types::{Callback, MaybeSend}; use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataStorage}; -use crate::util::{get_userdata, short_type_name}; +use crate::util::{ + borrow_userdata_scoped, borrow_userdata_scoped_mut, get_userdata, short_type_name, TypeIdHints, +}; use crate::value::Value; #[cfg(feature = "async")] @@ -21,38 +23,18 @@ use { std::future::{self, Future}, }; -#[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] -use std::rc::Rc; -#[cfg(feature = "userdata-wrappers")] -use std::sync::{Arc, Mutex, RwLock}; - #[derive(Clone, Copy)] -enum UserDataTypeId { - Shared(TypeId), +enum UserDataType { + Shared(TypeIdHints), Unique(*mut c_void), - - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - Rc(TypeId), - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - RcRefCell(TypeId), - #[cfg(feature = "userdata-wrappers")] - Arc(TypeId), - #[cfg(feature = "userdata-wrappers")] - ArcMutex(TypeId), - #[cfg(feature = "userdata-wrappers")] - ArcRwLock(TypeId), - #[cfg(feature = "userdata-wrappers")] - ArcParkingLotMutex(TypeId), - #[cfg(feature = "userdata-wrappers")] - ArcParkingLotRwLock(TypeId), } /// Handle to registry for userdata methods and metamethods. pub struct UserDataRegistry { lua: LuaGuard, raw: RawUserDataRegistry, - ud_type_id: UserDataTypeId, - _type: PhantomData, + r#type: UserDataType, + _phantom: PhantomData, } pub(crate) struct RawUserDataRegistry { @@ -75,46 +57,34 @@ pub(crate) struct RawUserDataRegistry { pub(crate) type_name: StdString, } -impl UserDataTypeId { +impl UserDataType { #[inline] - pub(crate) fn type_id(self) -> Option { + pub(crate) fn type_id(&self) -> Option { match self { - UserDataTypeId::Shared(type_id) => Some(type_id), - UserDataTypeId::Unique(_) => None, - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - UserDataTypeId::Rc(type_id) => Some(type_id), - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - UserDataTypeId::RcRefCell(type_id) => Some(type_id), - #[cfg(feature = "userdata-wrappers")] - UserDataTypeId::Arc(type_id) => Some(type_id), - #[cfg(feature = "userdata-wrappers")] - UserDataTypeId::ArcMutex(type_id) => Some(type_id), - #[cfg(feature = "userdata-wrappers")] - UserDataTypeId::ArcRwLock(type_id) => Some(type_id), - #[cfg(feature = "userdata-wrappers")] - UserDataTypeId::ArcParkingLotMutex(type_id) => Some(type_id), - #[cfg(feature = "userdata-wrappers")] - UserDataTypeId::ArcParkingLotRwLock(type_id) => Some(type_id), + UserDataType::Shared(hints) => Some(hints.type_id()), + UserDataType::Unique(_) => None, } } } #[cfg(feature = "send")] -unsafe impl Send for UserDataTypeId {} +unsafe impl Send for UserDataType {} + +impl UserDataRegistry { + #[inline(always)] + pub(crate) fn new(lua: &Lua) -> Self { + Self::with_type(lua, UserDataType::Shared(TypeIdHints::new::())) + } +} impl UserDataRegistry { - #[inline(always)] - pub(crate) fn new(lua: &Lua, type_id: TypeId) -> Self { - Self::with_type_id(lua, UserDataTypeId::Shared(type_id)) - } - #[inline(always)] pub(crate) fn new_unique(lua: &Lua, ud_ptr: *mut c_void) -> Self { - Self::with_type_id(lua, UserDataTypeId::Unique(ud_ptr)) + Self::with_type(lua, UserDataType::Unique(ud_ptr)) } #[inline(always)] - fn with_type_id(lua: &Lua, ud_type_id: UserDataTypeId) -> Self { + fn with_type(lua: &Lua, r#type: UserDataType) -> Self { let raw = RawUserDataRegistry { fields: Vec::new(), field_getters: Vec::new(), @@ -127,15 +97,15 @@ impl UserDataRegistry { #[cfg(feature = "async")] async_meta_methods: Vec::new(), destructor: super::util::userdata_destructor::, - type_id: ud_type_id.type_id(), + type_id: r#type.type_id(), type_name: short_type_name::(), }; UserDataRegistry { lua: lua.lock_arc(), raw, - ud_type_id, - _type: PhantomData, + r#type, + _phantom: PhantomData, } } @@ -152,7 +122,7 @@ impl UserDataRegistry { }; } - let target_type_id = self.ud_type_id; + let target_type = self.r#type; Box::new(move |rawlua, nargs| unsafe { if nargs == 0 { let err = Error::from_lua_conversion("missing argument", "userdata", None); @@ -164,18 +134,16 @@ impl UserDataRegistry { // Self was at position 1, so we pass 2 here let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua); - match target_type_id { + match target_type { #[rustfmt::skip] - UserDataTypeId::Shared(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { + UserDataType::Shared(type_hints) => { + let type_id = try_self_arg!(rawlua.get_userdata_type_id::(state, self_index)); + try_self_arg!(borrow_userdata_scoped(state, self_index, type_id, type_hints, |ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[rustfmt::skip] - UserDataTypeId::Unique(target_ptr) + UserDataType::Unique(target_ptr) if get_userdata::>(state, self_index) as *mut c_void == target_ptr => { let ud = target_ptr as *mut UserDataStorage; @@ -183,83 +151,6 @@ impl UserDataRegistry { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - #[rustfmt::skip] - UserDataTypeId::Rc(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - #[rustfmt::skip] - UserDataTypeId::RcRefCell(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; - method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::Arc(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcMutex(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; - method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcRwLock(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; - method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcParkingLotMutex(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) - == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?; - method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcParkingLotRwLock(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) - == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?; - method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua) - })) - } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } }) @@ -279,7 +170,7 @@ impl UserDataRegistry { } let method = RefCell::new(method); - let target_type_id = self.ud_type_id; + let target_type = self.r#type; Box::new(move |rawlua, nargs| unsafe { let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?; if nargs == 0 { @@ -292,18 +183,16 @@ impl UserDataRegistry { // Self was at position 1, so we pass 2 here let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua); - match target_type_id { + match target_type { #[rustfmt::skip] - UserDataTypeId::Shared(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped_mut(|ud| { + UserDataType::Shared(type_hints) => { + let type_id = try_self_arg!(rawlua.get_userdata_type_id::(state, self_index)); + try_self_arg!(borrow_userdata_scoped_mut(state, self_index, type_id, type_hints, |ud| { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } #[rustfmt::skip] - UserDataTypeId::Unique(target_ptr) + UserDataType::Unique(target_ptr) if get_userdata::>(state, self_index) as *mut c_void == target_ptr => { let ud = target_ptr as *mut UserDataStorage; @@ -311,77 +200,6 @@ impl UserDataRegistry { method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua) })) } - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - #[rustfmt::skip] - UserDataTypeId::Rc(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => - { - Err(Error::UserDataBorrowMutError) - }, - #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] - #[rustfmt::skip] - UserDataTypeId::RcRefCell(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?; - method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::Arc(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>(self_index)) == Some(target_type_id) => - { - Err(Error::UserDataBorrowMutError) - }, - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcMutex(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?; - method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcRwLock(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?; - method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcParkingLotMutex(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) - == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?; - method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) - })) - } - #[cfg(feature = "userdata-wrappers")] - #[rustfmt::skip] - UserDataTypeId::ArcParkingLotRwLock(target_type_id) - if try_self_arg!(rawlua.get_userdata_type_id::>>(self_index)) - == Some(target_type_id) => - { - let ud = get_userdata::>>>(state, self_index); - try_self_arg!((*ud).try_borrow_scoped(|ud| { - let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?; - method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua) - })) - } _ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)), } }) @@ -789,14 +607,10 @@ impl UserDataMethods for UserDataRegistry { } macro_rules! lua_userdata_impl { - ($type:ty => $type_variant:tt) => { - lua_userdata_impl!($type, UserDataTypeId::$type_variant(TypeId::of::<$type>())); - }; - - ($type:ty, $type_id:expr) => { + ($type:ty) => { impl UserData for $type { fn register(registry: &mut UserDataRegistry) { - let mut orig_registry = UserDataRegistry::with_type_id(registry.lua.lua(), $type_id); + let mut orig_registry = UserDataRegistry::new(registry.lua.lua()); T::register(&mut orig_registry); // Copy all fields, methods, etc. from the original registry @@ -818,22 +632,22 @@ macro_rules! lua_userdata_impl { // A special proxy object for UserData pub(crate) struct UserDataProxy(pub(crate) PhantomData); -lua_userdata_impl!(UserDataProxy, UserDataTypeId::Shared(TypeId::of::())); +lua_userdata_impl!(UserDataProxy); #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] -lua_userdata_impl!(Rc => Rc); +lua_userdata_impl!(std::rc::Rc); #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] -lua_userdata_impl!(Rc> => RcRefCell); +lua_userdata_impl!(std::rc::Rc>); #[cfg(feature = "userdata-wrappers")] -lua_userdata_impl!(Arc => Arc); +lua_userdata_impl!(std::sync::Arc); #[cfg(feature = "userdata-wrappers")] -lua_userdata_impl!(Arc> => ArcMutex); +lua_userdata_impl!(std::sync::Arc>); #[cfg(feature = "userdata-wrappers")] -lua_userdata_impl!(Arc> => ArcRwLock); +lua_userdata_impl!(std::sync::Arc>); #[cfg(feature = "userdata-wrappers")] -lua_userdata_impl!(Arc> => ArcParkingLotMutex); +lua_userdata_impl!(std::sync::Arc>); #[cfg(feature = "userdata-wrappers")] -lua_userdata_impl!(Arc> => ArcParkingLotRwLock); +lua_userdata_impl!(std::sync::Arc>); #[cfg(test)] mod assertions { diff --git a/src/util/mod.rs b/src/util/mod.rs index 48e7d8f..d53f1b7 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -12,8 +12,9 @@ pub(crate) use error::{ pub(crate) use short_names::short_type_name; pub(crate) use types::TypeKey; pub(crate) use userdata::{ - get_destructed_userdata_metatable, get_internal_metatable, get_internal_userdata, get_userdata, - init_internal_metatable, init_userdata_metatable, push_internal_userdata, take_userdata, + borrow_userdata_scoped, borrow_userdata_scoped_mut, get_destructed_userdata_metatable, + get_internal_metatable, get_internal_userdata, get_userdata, init_internal_metatable, + init_userdata_metatable, push_internal_userdata, take_userdata, TypeIdHints, DESTRUCTED_USERDATA_METATABLE, }; diff --git a/src/util/userdata.rs b/src/util/userdata.rs index 119b8c8..359dcf8 100644 --- a/src/util/userdata.rs +++ b/src/util/userdata.rs @@ -1,7 +1,9 @@ +use std::any::TypeId; use std::os::raw::{c_int, c_void}; use std::{ptr, str}; -use crate::error::Result; +use crate::error::{Error, Result}; +use crate::userdata::UserDataStorage; use crate::util::{check_stack, get_metatable_ptr, push_table, rawget_field, rawset_field, TypeKey}; // Pushes the userdata and attaches a metatable with __gc method. @@ -339,6 +341,199 @@ unsafe extern "C-unwind" fn userdata_destructor(state: *mut ffi::lua_State) - 0 } +// Userdata type hints, used to match types of wrapped userdata +#[derive(Clone, Copy)] +pub(crate) struct TypeIdHints { + t: TypeId, + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + rc: TypeId, + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + rc_refcell: TypeId, + + #[cfg(feature = "userdata-wrappers")] + arc: TypeId, + #[cfg(feature = "userdata-wrappers")] + arc_mutex: TypeId, + #[cfg(feature = "userdata-wrappers")] + arc_rwlock: TypeId, + #[cfg(feature = "userdata-wrappers")] + arc_pl_mutex: TypeId, + #[cfg(feature = "userdata-wrappers")] + arc_pl_rwlock: TypeId, +} + +impl TypeIdHints { + pub(crate) fn new() -> Self { + Self { + t: TypeId::of::(), + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + rc: TypeId::of::>(), + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + rc_refcell: TypeId::of::>>(), + + #[cfg(feature = "userdata-wrappers")] + arc: TypeId::of::>(), + #[cfg(feature = "userdata-wrappers")] + arc_mutex: TypeId::of::>>(), + #[cfg(feature = "userdata-wrappers")] + arc_rwlock: TypeId::of::>>(), + #[cfg(feature = "userdata-wrappers")] + arc_pl_mutex: TypeId::of::>>(), + #[cfg(feature = "userdata-wrappers")] + arc_pl_rwlock: TypeId::of::>>(), + } + } + + #[inline(always)] + pub(crate) fn type_id(&self) -> TypeId { + self.t + } +} + +pub(crate) unsafe fn borrow_userdata_scoped( + state: *mut ffi::lua_State, + idx: c_int, + type_id: Option, + type_hints: TypeIdHints, + f: impl FnOnce(&T) -> R, +) -> Result { + match type_id { + Some(type_id) if type_id == type_hints.t => { + let ud = get_userdata::>(state, idx); + (*ud).try_borrow_scoped(|ud| f(ud)) + } + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == type_hints.rc => { + let ud = get_userdata::>>(state, idx); + (*ud).try_borrow_scoped(|ud| f(ud)) + } + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == type_hints.rc_refcell => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped(|ud| { + let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?; + Ok(f(&ud)) + })? + } + + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc => { + let ud = get_userdata::>>(state, idx); + (*ud).try_borrow_scoped(|ud| f(ud)) + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_mutex => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped(|ud| { + let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?; + Ok(f(&ud)) + })? + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_rwlock => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped(|ud| { + let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?; + Ok(f(&ud)) + })? + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_pl_mutex => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped(|ud| { + let ud = ud.try_lock().ok_or(Error::UserDataBorrowError)?; + Ok(f(&ud)) + })? + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_pl_rwlock => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped(|ud| { + let ud = ud.try_read().ok_or(Error::UserDataBorrowError)?; + Ok(f(&ud)) + })? + } + _ => Err(Error::UserDataTypeMismatch), + } +} + +pub(crate) unsafe fn borrow_userdata_scoped_mut( + state: *mut ffi::lua_State, + idx: c_int, + type_id: Option, + type_hints: TypeIdHints, + f: impl FnOnce(&mut T) -> R, +) -> Result { + match type_id { + Some(type_id) if type_id == type_hints.t => { + let ud = get_userdata::>(state, idx); + (*ud).try_borrow_scoped_mut(|ud| f(ud)) + } + + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == type_hints.rc => { + let ud = get_userdata::>>(state, idx); + (*ud).try_borrow_scoped_mut(|ud| match std::rc::Rc::get_mut(ud) { + Some(ud) => Ok(f(ud)), + None => Err(Error::UserDataBorrowMutError), + })? + } + #[cfg(all(feature = "userdata-wrappers", not(feature = "send")))] + Some(type_id) if type_id == type_hints.rc_refcell => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped(|ud| { + let mut ud = ud.try_borrow_mut().map_err(|_| Error::UserDataBorrowMutError)?; + Ok(f(&mut ud)) + })? + } + + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc => { + let ud = get_userdata::>>(state, idx); + (*ud).try_borrow_scoped_mut(|ud| match std::sync::Arc::get_mut(ud) { + Some(ud) => Ok(f(ud)), + None => Err(Error::UserDataBorrowMutError), + })? + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_mutex => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped_mut(|ud| { + let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?; + Ok(f(&mut ud)) + })? + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_rwlock => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped_mut(|ud| { + let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?; + Ok(f(&mut ud)) + })? + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_pl_mutex => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped_mut(|ud| { + let mut ud = ud.try_lock().ok_or(Error::UserDataBorrowMutError)?; + Ok(f(&mut ud)) + })? + } + #[cfg(feature = "userdata-wrappers")] + Some(type_id) if type_id == type_hints.arc_pl_rwlock => { + let ud = get_userdata::>>>(state, idx); + (*ud).try_borrow_scoped_mut(|ud| { + let mut ud = ud.try_write().ok_or(Error::UserDataBorrowMutError)?; + Ok(f(&mut ud)) + })? + } + _ => Err(Error::UserDataTypeMismatch), + } +} + pub(crate) static DESTRUCTED_USERDATA_METATABLE: u8 = 0; static USERDATA_METATABLE_INDEX: u8 = 0; static USERDATA_METATABLE_NEWINDEX: u8 = 0; diff --git a/tests/userdata.rs b/tests/userdata.rs index 77dbfcf..c85b75d 100644 --- a/tests/userdata.rs +++ b/tests/userdata.rs @@ -913,6 +913,7 @@ fn test_nested_userdata_gc() -> Result<()> { #[cfg(feature = "userdata-wrappers")] #[test] fn test_userdata_wrappers() -> Result<()> { + #[derive(Debug)] struct MyUserData(i64); impl UserData for MyUserData { @@ -924,6 +925,10 @@ fn test_userdata_wrappers() -> Result<()> { Ok(()) }) } + + fn add_methods>(methods: &mut M) { + methods.add_method("dbg", |_, this, ()| Ok(format!("{this:?}"))); + } } let lua = Lua::new(); @@ -932,136 +937,359 @@ fn test_userdata_wrappers() -> Result<()> { // Rc #[cfg(not(feature = "send"))] { - let ud = std::rc::Rc::new(MyUserData(1)); - globals.set("rc_ud", ud.clone())?; + use std::rc::Rc; + + let ud = Rc::new(MyUserData(1)); + globals.set("ud", ud.clone())?; lua.load( r#" - assert(rc_ud.static == "constant") - local ok, err = pcall(function() rc_ud.data = 2 end) + assert(ud.static == "constant") + local ok, err = pcall(function() ud.data = 2 end) assert( - tostring(err):sub(1, 32) == "error mutably borrowing userdata", - "expected error mutably borrowing userdata, got " .. tostring(err) + tostring(err):find("error mutably borrowing userdata") ~= nil, + "expected 'error mutably borrowing userdata', got '" .. tostring(err) .. "'" ) - assert(rc_ud.data == 1) + assert(ud.data == 1) + assert(ud:dbg(), "MyUserData(1)") "#, ) .exec() .unwrap(); - globals.set("rc_ud", Nil)?; + + // Test borrowing original userdata + { + let ud = globals.get::("ud")?; + assert!(ud.is::>()); + assert!(!ud.is::()); + + assert_eq!(ud.borrow::()?.0, 1); + assert!(matches!( + ud.borrow_mut::(), + Err(Error::UserDataBorrowMutError) + )); + assert!(ud.borrow_mut::>().is_ok()); + + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 1); + assert!(matches!( + ud.borrow_mut_scoped::(|_| ()), + Err(Error::UserDataBorrowMutError) + )); + } + + // Collect userdata + globals.set("ud", Nil)?; lua.gc_collect()?; - assert_eq!(std::rc::Rc::strong_count(&ud), 1); + assert_eq!(Rc::strong_count(&ud), 1); + + // We must be able to mutate userdata when having one reference only + globals.set("ud", ud)?; + lua.load( + r#" + ud.data = 2 + assert(ud.data == 2) + "#, + ) + .exec() + .unwrap(); } // Rc> #[cfg(not(feature = "send"))] { - let ud = std::rc::Rc::new(std::cell::RefCell::new(MyUserData(2))); - globals.set("rc_refcell_ud", ud.clone())?; + use std::cell::RefCell; + use std::rc::Rc; + + let ud = Rc::new(RefCell::new(MyUserData(2))); + globals.set("ud", ud.clone())?; lua.load( r#" - assert(rc_refcell_ud.static == "constant") - rc_refcell_ud.data = rc_refcell_ud.data + 1 - assert(rc_refcell_ud.data == 3) - "#, + assert(ud.static == "constant") + assert(ud.data == 2) + ud.data = 10 + assert(ud.data == 10) + assert(ud:dbg() == "MyUserData(10)") + "#, ) - .exec()?; - assert_eq!(ud.borrow().0, 3); - globals.set("rc_refcell_ud", Nil)?; + .exec() + .unwrap(); + + // Test borrowing original userdata + { + let ud = globals.get::("ud")?; + assert!(ud.is::>>()); + assert!(!ud.is::()); + + assert_eq!(ud.borrow::()?.0, 10); + assert_eq!(ud.borrow_mut::()?.0, 10); + ud.borrow_mut::()?.0 = 20; + assert_eq!(ud.borrow::()?.0, 20); + + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 20); + ud.borrow_mut_scoped::(|x| x.0 = 30)?; + assert_eq!(ud.borrow::()?.0, 30); + + // Double (read) borrow is okay + let _borrow = ud.borrow::()?; + assert_eq!(ud.borrow::()?.0, 30); + assert!(matches!( + ud.borrow_mut::(), + Err(Error::UserDataBorrowMutError) + )); + } + + // Collect userdata + globals.set("ud", Nil)?; lua.gc_collect()?; - assert_eq!(std::rc::Rc::strong_count(&ud), 1); + assert_eq!(Rc::strong_count(&ud), 1); + + // Check destroying wrapped UserDataRef without references in Lua + let ud = lua.convert::>(ud)?; + lua.gc_collect()?; + assert_eq!(ud.0, 30); + drop(ud); } // Arc { let ud = Arc::new(MyUserData(3)); - globals.set("arc_ud", ud.clone())?; + globals.set("ud", ud.clone())?; lua.load( r#" - assert(arc_ud.static == "constant") - local ok, err = pcall(function() arc_ud.data = 10 end) + assert(ud.static == "constant") + local ok, err = pcall(function() ud.data = 4 end) assert( - tostring(err):sub(1, 32) == "error mutably borrowing userdata", - "expected error mutably borrowing userdata, got " .. tostring(err) + tostring(err):find("error mutably borrowing userdata") ~= nil, + "expected 'error mutably borrowing userdata', got '" .. tostring(err) .. "'" ) - assert(arc_ud.data == 3) - "#, + assert(ud.data == 3) + assert(ud:dbg() == "MyUserData(3)") + "#, ) - .exec()?; - globals.set("arc_ud", Nil)?; + .exec() + .unwrap(); + + // Test borrowing original userdata + { + let ud = globals.get::("ud")?; + assert!(ud.is::>()); + assert!(!ud.is::()); + + assert_eq!(ud.borrow::()?.0, 3); + assert!(matches!( + ud.borrow_mut::(), + Err(Error::UserDataBorrowMutError) + )); + assert!(ud.borrow_mut::>().is_ok()); + + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 3); + assert!(matches!( + ud.borrow_mut_scoped::(|_| ()), + Err(Error::UserDataBorrowMutError) + )); + } + + // Collect userdata + globals.set("ud", Nil)?; lua.gc_collect()?; assert_eq!(Arc::strong_count(&ud), 1); + + // We must be able to mutate userdata when having one reference only + globals.set("ud", ud)?; + lua.load( + r#" + ud.data = 4 + assert(ud.data == 4) + "#, + ) + .exec() + .unwrap(); } // Arc> { - let ud = Arc::new(std::sync::Mutex::new(MyUserData(4))); - globals.set("arc_mutex_ud", ud.clone())?; + use std::sync::Mutex; + + let ud = Arc::new(Mutex::new(MyUserData(5))); + globals.set("ud", ud.clone())?; lua.load( r#" - assert(arc_mutex_ud.static == "constant") - arc_mutex_ud.data = arc_mutex_ud.data + 1 - assert(arc_mutex_ud.data == 5) - "#, + assert(ud.static == "constant") + assert(ud.data == 5) + ud.data = 6 + assert(ud.data == 6) + assert(ud:dbg() == "MyUserData(6)") + "#, ) - .exec()?; - assert_eq!(ud.lock().unwrap().0, 5); - globals.set("arc_mutex_ud", Nil)?; + .exec() + .unwrap(); + + // Test borrowing original userdata + { + let ud = globals.get::("ud")?; + assert!(ud.is::>>()); + assert!(!ud.is::()); + + #[rustfmt::skip] + assert!(matches!(ud.borrow::(), Err(Error::UserDataTypeMismatch))); + #[rustfmt::skip] + assert!(matches!(ud.borrow_mut::(), Err(Error::UserDataTypeMismatch))); + + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 6); + ud.borrow_mut_scoped::(|x| x.0 = 8)?; + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 8); + } + + // Collect userdata + globals.set("ud", Nil)?; lua.gc_collect()?; assert_eq!(Arc::strong_count(&ud), 1); } // Arc> { - let ud = Arc::new(std::sync::RwLock::new(MyUserData(6))); - globals.set("arc_rwlock_ud", ud.clone())?; + use std::sync::RwLock; + + let ud = Arc::new(RwLock::new(MyUserData(9))); + globals.set("ud", ud.clone())?; lua.load( r#" - assert(arc_rwlock_ud.static == "constant") - arc_rwlock_ud.data = arc_rwlock_ud.data + 1 - assert(arc_rwlock_ud.data == 7) - "#, + assert(ud.static == "constant") + assert(ud.data == 9) + ud.data = 10 + assert(ud.data == 10) + assert(ud:dbg() == "MyUserData(10)") + "#, ) - .exec()?; - assert_eq!(ud.read().unwrap().0, 7); - globals.set("arc_rwlock_ud", Nil)?; + .exec() + .unwrap(); + + // Test borrowing original userdata + { + let ud = globals.get::("ud")?; + assert!(ud.is::>>()); + assert!(!ud.is::()); + + #[rustfmt::skip] + assert!(matches!(ud.borrow::(), Err(Error::UserDataTypeMismatch))); + #[rustfmt::skip] + assert!(matches!(ud.borrow_mut::(), Err(Error::UserDataTypeMismatch))); + + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 10); + ud.borrow_mut_scoped::(|x| x.0 = 12)?; + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 12); + } + + // Collect userdata + globals.set("ud", Nil)?; lua.gc_collect()?; assert_eq!(Arc::strong_count(&ud), 1); } // Arc> { - let ud = Arc::new(parking_lot::Mutex::new(MyUserData(8))); - globals.set("arc_parking_lot_mutex_ud", ud.clone())?; + use parking_lot::Mutex; + + let ud = Arc::new(Mutex::new(MyUserData(13))); + globals.set("ud", ud.clone())?; lua.load( r#" - assert(arc_parking_lot_mutex_ud.static == "constant") - arc_parking_lot_mutex_ud.data = arc_parking_lot_mutex_ud.data + 1 - assert(arc_parking_lot_mutex_ud.data == 9) - "#, + assert(ud.static == "constant") + assert(ud.data == 13) + ud.data = 14 + assert(ud.data == 14) + assert(ud:dbg() == "MyUserData(14)") + "#, ) - .exec()?; - assert_eq!(ud.lock().0, 9); - globals.set("arc_parking_lot_mutex_ud", Nil)?; + .exec() + .unwrap(); + + // Test borrowing original userdata + { + let ud = globals.get::("ud")?; + assert!(ud.is::>>()); + assert!(!ud.is::()); + + assert_eq!(ud.borrow::()?.0, 14); + assert_eq!(ud.borrow_mut::()?.0, 14); + ud.borrow_mut::()?.0 = 15; + assert_eq!(ud.borrow::()?.0, 15); + + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 15); + ud.borrow_mut_scoped::(|x| x.0 = 16)?; + assert_eq!(ud.borrow::()?.0, 16); + + // Double borrow is not allowed + let _borrow = ud.borrow::()?; + assert!(matches!( + ud.borrow::(), + Err(Error::UserDataBorrowError) + )); + } + + // Collect userdata + globals.set("ud", Nil)?; lua.gc_collect()?; assert_eq!(Arc::strong_count(&ud), 1); + + // Check destroying wrapped UserDataRef without references in Lua + let ud = lua.convert::>(ud)?; + lua.gc_collect()?; + assert_eq!(ud.0, 16); + drop(ud); } // Arc> { - let ud = Arc::new(parking_lot::RwLock::new(MyUserData(10))); - globals.set("arc_parking_lot_rwlock_ud", ud.clone())?; + use parking_lot::RwLock; + + let ud = Arc::new(RwLock::new(MyUserData(17))); + globals.set("ud", ud.clone())?; lua.load( r#" - assert(arc_parking_lot_rwlock_ud.static == "constant") - arc_parking_lot_rwlock_ud.data = arc_parking_lot_rwlock_ud.data + 1 - assert(arc_parking_lot_rwlock_ud.data == 11) - "#, + assert(ud.static == "constant") + assert(ud.data == 17) + ud.data = 18 + assert(ud.data == 18) + assert(ud:dbg() == "MyUserData(18)") + "#, ) - .exec()?; - assert_eq!(ud.read().0, 11); - globals.set("arc_parking_lot_rwlock_ud", Nil)?; + .exec() + .unwrap(); + + // Test borrowing original userdata + { + let ud = globals.get::("ud")?; + assert!(ud.is::>>()); + assert!(!ud.is::()); + + assert_eq!(ud.borrow::()?.0, 18); + assert_eq!(ud.borrow_mut::()?.0, 18); + ud.borrow_mut::()?.0 = 19; + assert_eq!(ud.borrow::()?.0, 19); + + assert_eq!(ud.borrow_scoped::(|x| x.0)?, 19); + ud.borrow_mut_scoped::(|x| x.0 = 20)?; + assert_eq!(ud.borrow::()?.0, 20); + + // Multiple read borrows are allowed with parking_lot::RwLock + let _borrow1 = ud.borrow::()?; + let _borrow2 = ud.borrow::()?; + assert!(matches!( + ud.borrow_mut::(), + Err(Error::UserDataBorrowMutError) + )); + } + + // Collect userdata + globals.set("ud", Nil)?; lua.gc_collect()?; assert_eq!(Arc::strong_count(&ud), 1); + + // Check destroying wrapped UserDataRef without references in Lua + let ud = lua.convert::>(ud)?; + lua.gc_collect()?; + assert_eq!(ud.0, 20); + drop(ud); } Ok(())