Skip to content

Commit 4fe7d15

Browse files
committed
Refactor userdata-wrappers feature.
Support borrowing underlying data in `UserDataRef` and `UserDataRefMut`.
1 parent edbf1e9 commit 4fe7d15

File tree

10 files changed

+1144
-642
lines changed

10 files changed

+1144
-642
lines changed

src/state.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1330,7 +1330,7 @@ impl Lua {
13301330
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
13311331
pub fn register_userdata_type<T: 'static>(&self, f: impl FnOnce(&mut UserDataRegistry<T>)) -> Result<()> {
13321332
let type_id = TypeId::of::<T>();
1333-
let mut registry = UserDataRegistry::new(self, type_id);
1333+
let mut registry = UserDataRegistry::new(self);
13341334
f(&mut registry);
13351335

13361336
let lua = self.lock();
@@ -1499,7 +1499,6 @@ impl Lua {
14991499
&self,
15001500
f: impl for<'scope> FnOnce(&'scope Scope<'scope, 'env>) -> Result<R>,
15011501
) -> Result<R> {
1502-
// TODO: Update to `&Scope` in next major release
15031502
f(&Scope::new(self.lock_arc()))
15041503
}
15051504

src/state/raw.rs

+13-8
Original file line numberDiff line numberDiff line change
@@ -864,7 +864,7 @@ impl RawLua {
864864
}
865865

866866
// Create a new metatable from `UserData` definition
867-
let mut registry = UserDataRegistry::new(self.lua(), type_id);
867+
let mut registry = UserDataRegistry::new(self.lua());
868868
T::register(&mut registry);
869869

870870
self.create_userdata_metatable(registry.into_raw())
@@ -885,7 +885,7 @@ impl RawLua {
885885
// Check if metatable creation is pending or create an empty metatable otherwise
886886
let registry = match (*self.extra.get()).pending_userdata_reg.remove(&type_id) {
887887
Some(registry) => registry,
888-
None => UserDataRegistry::<T>::new(self.lua(), type_id).into_raw(),
888+
None => UserDataRegistry::<T>::new(self.lua()).into_raw(),
889889
};
890890
self.create_userdata_metatable(registry)
891891
})
@@ -1103,17 +1103,22 @@ impl RawLua {
11031103
// Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
11041104
//
11051105
// Returns `None` if the userdata is registered but non-static.
1106-
pub(crate) unsafe fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
1107-
self.get_userdata_type_id_inner(self.ref_thread(), vref.index)
1106+
#[inline(always)]
1107+
pub(crate) fn get_userdata_ref_type_id(&self, vref: &ValueRef) -> Result<Option<TypeId>> {
1108+
unsafe { self.get_userdata_type_id_inner(self.ref_thread(), vref.index) }
11081109
}
11091110

11101111
// Same as `get_userdata_ref_type_id` but assumes the userdata is already on the stack.
1111-
pub(crate) unsafe fn get_userdata_type_id<T>(&self, idx: c_int) -> Result<Option<TypeId>> {
1112-
match self.get_userdata_type_id_inner(self.state(), idx) {
1112+
pub(crate) unsafe fn get_userdata_type_id<T>(
1113+
&self,
1114+
state: *mut ffi::lua_State,
1115+
idx: c_int,
1116+
) -> Result<Option<TypeId>> {
1117+
match self.get_userdata_type_id_inner(state, idx) {
11131118
Ok(type_id) => Ok(type_id),
1114-
Err(Error::UserDataTypeMismatch) if ffi::lua_type(self.state(), idx) != ffi::LUA_TUSERDATA => {
1119+
Err(Error::UserDataTypeMismatch) if ffi::lua_type(state, idx) != ffi::LUA_TUSERDATA => {
11151120
// Report `FromLuaConversionError` instead
1116-
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(self.state(), idx));
1121+
let idx_type_name = CStr::from_ptr(ffi::luaL_typename(state, idx));
11171122
let idx_type_name = idx_type_name.to_str().unwrap();
11181123
let message = format!("expected userdata of type '{}'", short_type_name::<T>());
11191124
Err(Error::from_lua_conversion(idx_type_name, "userdata", message))

src/userdata.rs

+27-28
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,10 @@ use crate::string::String;
1212
use crate::table::{Table, TablePairs};
1313
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
1414
use crate::types::{MaybeSend, ValueRef};
15-
use crate::util::{check_stack, get_userdata, push_string, take_userdata, StackGuard};
15+
use crate::util::{
16+
borrow_userdata_scoped, borrow_userdata_scoped_mut, check_stack, get_userdata, push_string,
17+
take_userdata, StackGuard, TypeIdHints,
18+
};
1619
use crate::value::Value;
1720

1821
#[cfg(feature = "async")]
@@ -26,7 +29,7 @@ use {
2629

2730
// Re-export for convenience
2831
pub(crate) use cell::UserDataStorage;
29-
pub use cell::{UserDataRef, UserDataRefMut};
32+
pub use r#ref::{UserDataRef, UserDataRefMut};
3033
pub use registry::UserDataRegistry;
3134
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
3235

@@ -622,7 +625,10 @@ impl AnyUserData {
622625
/// Checks whether the type of this userdata is `T`.
623626
#[inline]
624627
pub fn is<T: 'static>(&self) -> bool {
625-
self.inspect::<T, _, _>(|_| Ok(())).is_ok()
628+
let lua = self.0.lua.lock();
629+
let type_id = lua.get_userdata_ref_type_id(&self.0);
630+
// We do not use wrapped types here, rather prefer to check the "real" type of the userdata
631+
matches!(type_id, Ok(Some(type_id)) if type_id == TypeId::of::<T>())
626632
}
627633

628634
/// Borrow this userdata immutably if it is of type `T`.
@@ -637,15 +643,19 @@ impl AnyUserData {
637643
/// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch
638644
#[inline]
639645
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
640-
self.inspect(|ud| ud.try_borrow_owned())
646+
let lua = self.0.lua.lock();
647+
unsafe { UserDataRef::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
641648
}
642649

643650
/// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
644651
/// to the closure.
645652
///
646653
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
647654
pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
648-
self.inspect(|ud| ud.try_borrow_scoped(|ud| f(ud)))
655+
let lua = self.0.lua.lock();
656+
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
657+
let type_hints = TypeIdHints::new::<T>();
658+
unsafe { borrow_userdata_scoped(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
649659
}
650660

651661
/// Borrow this userdata mutably if it is of type `T`.
@@ -660,15 +670,19 @@ impl AnyUserData {
660670
/// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch
661671
#[inline]
662672
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
663-
self.inspect(|ud| ud.try_borrow_owned_mut())
673+
let lua = self.0.lua.lock();
674+
unsafe { UserDataRefMut::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
664675
}
665676

666677
/// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
667678
/// to the closure.
668679
///
669680
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
670681
pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
671-
self.inspect(|ud| ud.try_borrow_scoped_mut(|ud| f(ud)))
682+
let lua = self.0.lua.lock();
683+
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
684+
let type_hints = TypeIdHints::new::<T>();
685+
unsafe { borrow_userdata_scoped_mut(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
672686
}
673687

674688
/// Takes the value out of this userdata.
@@ -687,9 +701,11 @@ impl AnyUserData {
687701
let type_id = lua.push_userdata_ref(&self.0)?;
688702
match type_id {
689703
Some(type_id) if type_id == TypeId::of::<T>() => {
690-
// Try to borrow userdata exclusively
691-
let _ = (*get_userdata::<UserDataStorage<T>>(state, -1)).try_borrow_mut()?;
692-
take_userdata::<UserDataStorage<T>>(state).into_inner()
704+
if (*get_userdata::<UserDataStorage<T>>(state, -1)).has_exclusive_access() {
705+
take_userdata::<UserDataStorage<T>>(state).into_inner()
706+
} else {
707+
Err(Error::UserDataBorrowMutError)
708+
}
693709
}
694710
_ => Err(Error::UserDataTypeMismatch),
695711
}
@@ -965,24 +981,6 @@ impl AnyUserData {
965981
};
966982
is_serializable().unwrap_or(false)
967983
}
968-
969-
pub(crate) fn inspect<T, F, R>(&self, func: F) -> Result<R>
970-
where
971-
T: 'static,
972-
F: FnOnce(&UserDataStorage<T>) -> Result<R>,
973-
{
974-
let lua = self.0.lua.lock();
975-
unsafe {
976-
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
977-
match type_id {
978-
Some(type_id) if type_id == TypeId::of::<T>() => {
979-
let ud = get_userdata::<UserDataStorage<T>>(lua.ref_thread(), self.0.index);
980-
func(&*ud)
981-
}
982-
_ => Err(Error::UserDataTypeMismatch),
983-
}
984-
}
985-
}
986984
}
987985

988986
/// Handle to a [`AnyUserData`] metatable.
@@ -1106,6 +1104,7 @@ where
11061104
mod cell;
11071105
mod lock;
11081106
mod object;
1107+
mod r#ref;
11091108
mod registry;
11101109
mod util;
11111110

0 commit comments

Comments
 (0)