summaryrefslogtreecommitdiff
path: root/src/userdata.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/userdata.rs')
-rw-r--r--src/userdata.rs86
1 files changed, 61 insertions, 25 deletions
diff --git a/src/userdata.rs b/src/userdata.rs
index d0518ee..e54eb33 100644
--- a/src/userdata.rs
+++ b/src/userdata.rs
@@ -11,7 +11,6 @@ use std::future::Future;
#[cfg(feature = "serialize")]
use {
serde::ser::{self, Serialize, Serializer},
- std::os::raw::c_void,
std::result::Result as StdResult,
};
@@ -21,7 +20,7 @@ use crate::function::Function;
use crate::lua::Lua;
use crate::table::{Table, TablePairs};
use crate::types::{Callback, LuaRef, MaybeSend};
-use crate::util::{check_stack, get_userdata, StackGuard};
+use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
@@ -626,18 +625,24 @@ impl<T> UserDataCell<T> {
.map(|r| RefMut::map(r, |r| r.deref_mut()))
.map_err(|_| Error::UserDataBorrowMutError)
}
+
+ // Consumes this `UserDataCell`, returning the wrapped value.
+ #[inline]
+ fn into_inner(self) -> T {
+ self.0.into_inner().into_inner()
+ }
}
pub(crate) enum UserDataWrapped<T> {
- Default(T),
+ Default(Box<T>),
#[cfg(feature = "serialize")]
- Serializable(*mut T, *const dyn erased_serde::Serialize),
+ Serializable(Box<dyn erased_serde::Serialize>),
}
impl<T> UserDataWrapped<T> {
#[inline]
fn new(data: T) -> Self {
- UserDataWrapped::Default(data)
+ UserDataWrapped::Default(Box::new(data))
}
#[cfg(feature = "serialize")]
@@ -646,16 +651,15 @@ impl<T> UserDataWrapped<T> {
where
T: 'static + Serialize,
{
- let data_raw = Box::into_raw(Box::new(data));
- UserDataWrapped::Serializable(data_raw, data_raw)
+ UserDataWrapped::Serializable(Box::new(data))
}
-}
-#[cfg(feature = "serialize")]
-impl<T> Drop for UserDataWrapped<T> {
- fn drop(&mut self) {
- if let UserDataWrapped::Serializable(data, _) = *self {
- drop(unsafe { Box::from_raw(data) });
+ #[inline]
+ fn into_inner(self) -> T {
+ match self {
+ Self::Default(data) => *data,
+ #[cfg(feature = "serialize")]
+ Self::Serializable(data) => unsafe { *Box::from_raw(Box::into_raw(data) as *mut T) },
}
}
}
@@ -668,7 +672,9 @@ impl<T> Deref for UserDataWrapped<T> {
match self {
Self::Default(data) => data,
#[cfg(feature = "serialize")]
- Self::Serializable(data, _) => unsafe { &**data },
+ Self::Serializable(data) => unsafe {
+ &*(data.as_ref() as *const _ as *const Self::Target)
+ },
}
}
}
@@ -679,7 +685,9 @@ impl<T> DerefMut for UserDataWrapped<T> {
match self {
Self::Default(data) => data,
#[cfg(feature = "serialize")]
- Self::Serializable(data, _) => unsafe { &mut **data },
+ Self::Serializable(data) => unsafe {
+ &mut *(data.as_mut() as *mut _ as *mut Self::Target)
+ },
}
}
}
@@ -748,6 +756,35 @@ impl<'lua> AnyUserData<'lua> {
self.inspect(|cell| cell.try_borrow_mut())
}
+ /// Takes out the value of `UserData` and sets the special "destructed" metatable that prevents
+ /// any further operations with this userdata.
+ #[doc(hidden)]
+ pub fn take<T: 'static + UserData>(&self) -> Result<T> {
+ let lua = self.0.lua;
+ unsafe {
+ let _sg = StackGuard::new(lua.state);
+ check_stack(lua.state, 2)?;
+
+ let type_id = lua.push_userdata_ref(&self.0)?;
+ match type_id {
+ Some(type_id) if type_id == TypeId::of::<T>() => {
+ // Try to borrow userdata exclusively
+ let _ = (*get_userdata::<UserDataCell<T>>(lua.state, -1)).try_borrow_mut()?;
+
+ // Clear uservalue
+ #[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
+ ffi::lua_pushnil(lua.state);
+ #[cfg(any(feature = "lua51", feature = "luajit"))]
+ protect_lua!(lua.state, 0, 1, fn(state) ffi::lua_newtable(state))?;
+ ffi::lua_setuservalue(lua.state, -2);
+
+ Ok(take_userdata::<UserDataCell<T>>(lua.state).into_inner())
+ }
+ _ => Err(Error::UserDataTypeMismatch),
+ }
+ }
+ }
+
/// Sets an associated value to this `AnyUserData`.
///
/// The value may be any Lua value whatsoever, and can be retrieved with [`get_user_value`].
@@ -960,20 +997,19 @@ impl<'lua> Serialize for AnyUserData<'lua> {
where
S: Serializer,
{
- unsafe {
- let lua = self.0.lua;
+ let lua = self.0.lua;
+ let data = unsafe {
let _sg = StackGuard::new(lua.state);
check_stack(lua.state, 3).map_err(ser::Error::custom)?;
lua.push_userdata_ref(&self.0).map_err(ser::Error::custom)?;
- let ud = &*get_userdata::<UserDataCell<c_void>>(lua.state, -1);
- let data =
- ud.0.try_borrow()
- .map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?;
- match *data {
- UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
- UserDataWrapped::Serializable(_, ser) => (&*ser).serialize(serializer),
- }
+ let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
+ ud.0.try_borrow()
+ .map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
+ };
+ match &*data {
+ UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
+ UserDataWrapped::Serializable(ser) => ser.serialize(serializer),
}
}
}