summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs1
-rw-r--r--src/lua.rs42
-rw-r--r--src/prelude.rs2
-rw-r--r--src/scope.rs220
-rw-r--r--src/userdata.rs82
-rw-r--r--tests/scope.rs104
6 files changed, 322 insertions, 129 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 1a5ea36..b4597b5 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -121,6 +121,7 @@ pub use crate::types::{Integer, LightUserData, Number, RegistryKey};
pub use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMetatable, UserDataMethods,
};
+pub use crate::userdata_impl::UserDataRegistrar;
pub use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
#[cfg(not(feature = "luau"))]
diff --git a/src/lua.rs b/src/lua.rs
index 26bec0e..b26dfe0 100644
--- a/src/lua.rs
+++ b/src/lua.rs
@@ -1763,23 +1763,12 @@ impl Lua {
/// Otherwise, the userdata object will have an empty metatable.
///
/// All userdata instances of the same type `T` shares the same metatable.
+ #[inline]
pub fn create_any_userdata<T>(&self, data: T) -> Result<AnyUserData>
where
T: MaybeSend + 'static,
{
- unsafe {
- self.make_userdata_with_metatable(UserDataCell::new(data), || {
- // Check if userdata/metatable is already registered
- let type_id = TypeId::of::<T>();
- if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
- return Ok(table_id as Integer);
- }
-
- // Create empty metatable
- let registry = UserDataRegistrar::new();
- self.register_userdata_metatable::<T>(registry)
- })
- }
+ unsafe { self.make_any_userdata(UserDataCell::new(data)) }
}
/// Registers a custom Rust type in Lua to use in userdata objects.
@@ -1891,12 +1880,10 @@ impl Lua {
/// dropped. `Function` types will error when called, and `AnyUserData` will be typeless. It
/// would be impossible to prevent handles to scoped values from escaping anyway, since you
/// would always be able to smuggle them through Lua state.
- pub fn scope<'lua, 'scope, R, F>(&'lua self, f: F) -> Result<R>
- where
- 'lua: 'scope,
- R: 'static,
- F: FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
- {
+ pub fn scope<'lua, 'scope, R>(
+ &'lua self,
+ f: impl FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
+ ) -> Result<R> {
f(&Scope::new(self))
}
@@ -2961,6 +2948,23 @@ impl Lua {
})
}
+ pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData>
+ where
+ T: 'static,
+ {
+ self.make_userdata_with_metatable(data, || {
+ // Check if userdata/metatable is already registered
+ let type_id = TypeId::of::<T>();
+ if let Some(&table_id) = (*self.extra.get()).registered_userdata.get(&type_id) {
+ return Ok(table_id as Integer);
+ }
+
+ // Create empty metatable
+ let registry = UserDataRegistrar::new();
+ self.register_userdata_metatable::<T>(registry)
+ })
+ }
+
unsafe fn make_userdata_with_metatable<T>(
&self,
data: UserDataCell<T>,
diff --git a/src/prelude.rs b/src/prelude.rs
index 9111282..bf88bc8 100644
--- a/src/prelude.rs
+++ b/src/prelude.rs
@@ -12,7 +12,7 @@ pub use crate::{
TableSequence as LuaTableSequence, Thread as LuaThread, ThreadStatus as LuaThreadStatus,
UserData as LuaUserData, UserDataFields as LuaUserDataFields,
UserDataMetatable as LuaUserDataMetatable, UserDataMethods as LuaUserDataMethods,
- Value as LuaValue,
+ UserDataRegistrar as LuaUserDataRegistrar, Value as LuaValue,
};
#[cfg(not(feature = "luau"))]
diff --git a/src/scope.rs b/src/scope.rs
index 22bebc7..4dce3ef 100644
--- a/src/scope.rs
+++ b/src/scope.rs
@@ -2,8 +2,7 @@ use std::any::Any;
use std::cell::{Cell, RefCell};
use std::marker::PhantomData;
use std::mem;
-use std::os::raw::{c_int, c_void};
-use std::rc::Rc;
+use std::os::raw::c_int;
#[cfg(feature = "serialize")]
use serde::Serialize;
@@ -66,7 +65,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
where
A: FromLuaMulti<'callback>,
R: IntoLuaMulti<'callback>,
- F: 'scope + Fn(&'callback Lua, A) -> Result<R>,
+ F: Fn(&'callback Lua, A) -> Result<R> + 'scope,
{
// Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the
// callback itself must be 'scope lifetime, so the function should not be able to capture
@@ -99,7 +98,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
where
A: FromLuaMulti<'callback>,
R: IntoLuaMulti<'callback>,
- F: 'scope + FnMut(&'callback Lua, A) -> Result<R>,
+ F: FnMut(&'callback Lua, A) -> Result<R> + 'scope,
{
let func = RefCell::new(func);
self.create_function(move |lua, args| {
@@ -144,7 +143,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
}
}
- /// Create a Lua userdata object from a custom userdata type.
+ /// Creates a Lua userdata object from a custom userdata type.
///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send (but still requires that the
@@ -155,12 +154,18 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// [`Lua::scope`]: crate::Lua::scope
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
- T: 'static + UserData,
+ T: UserData + 'static,
{
- self.create_userdata_inner(UserDataCell::new(data))
+ // Safe even though T may not be Send, because the parent Lua cannot be sent to another
+ // thread while the Scope is alive (or the returned AnyUserData handle even).
+ unsafe {
+ let ud = self.lua.make_userdata(UserDataCell::new(data))?;
+ self.seal_userdata::<T>(&ud)?;
+ Ok(ud)
+ }
}
- /// Create a Lua userdata object from a custom serializable userdata type.
+ /// Creates a Lua userdata object from a custom serializable userdata type.
///
/// This is a version of [`Lua::create_ser_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send (but still requires that the
@@ -175,60 +180,125 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
#[cfg_attr(docsrs, doc(cfg(feature = "serialize")))]
pub fn create_ser_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
- T: 'static + UserData + Serialize,
+ T: UserData + Serialize + 'static,
{
- self.create_userdata_inner(UserDataCell::new_ser(data))
+ unsafe {
+ let ud = self.lua.make_userdata(UserDataCell::new_ser(data))?;
+ self.seal_userdata::<T>(&ud)?;
+ Ok(ud)
+ }
}
- fn create_userdata_inner<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData<'lua>>
+ /// Creates a Lua userdata object from a reference to custom userdata type.
+ ///
+ /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
+ /// scope drop, and does not require that the userdata type be Send. This method takes non-'static
+ /// reference to the data. See [`Lua::scope`] for more details.
+ ///
+ /// Userdata created with this method will not be able to be mutated from Lua.
+ pub fn create_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
where
- T: 'static + UserData,
+ T: UserData + 'static,
{
- // Safe even though T may not be Send, because the parent Lua cannot be sent to another
- // thread while the Scope is alive (or the returned AnyUserData handle even).
unsafe {
- let ud = self.lua.make_userdata(data)?;
-
- #[cfg(any(feature = "lua51", feature = "luajit"))]
- let newtable = self.lua.create_table()?;
- let destructor: DestructorCallback = Box::new(move |ud| {
- let state = ud.lua.state();
- let _sg = StackGuard::new(state);
- assert_stack(state, 2);
-
- // Check that userdata is not destructed (via `take()` call)
- if ud.lua.push_userdata_ref(&ud).is_err() {
- return vec![];
- }
+ let ud = self.lua.make_userdata(UserDataCell::new_ref(data))?;
+ self.seal_userdata::<T>(&ud)?;
+ Ok(ud)
+ }
+ }
- // Clear associated user values
- #[cfg(feature = "lua54")]
- for i in 1..=USER_VALUE_MAXSLOT {
- ffi::lua_pushnil(state);
- ffi::lua_setiuservalue(state, -2, i as c_int);
- }
- #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
- {
- ffi::lua_pushnil(state);
- ffi::lua_setuservalue(state, -2);
- }
- #[cfg(any(feature = "lua51", feature = "luajit"))]
- {
- ud.lua.push_ref(&newtable.0);
- ffi::lua_setuservalue(state, -2);
- }
+ /// Creates a Lua userdata object from a mutable reference to custom userdata type.
+ ///
+ /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
+ /// scope drop, and does not require that the userdata type be Send. This method takes non-'static
+ /// mutable reference to the data. See [`Lua::scope`] for more details.
+ pub fn create_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
+ where
+ T: UserData + 'static,
+ {
+ unsafe {
+ let ud = self.lua.make_userdata(UserDataCell::new_ref_mut(data))?;
+ self.seal_userdata::<T>(&ud)?;
+ Ok(ud)
+ }
+ }
- vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
- });
- self.destructors
- .borrow_mut()
- .push((ud.0.clone(), destructor));
+ /// Creates a Lua userdata object from a reference to custom Rust type.
+ ///
+ /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
+ /// scope drop, and does not require that the Rust type be Send. This method takes non-'static
+ /// reference to the data. See [`Lua::scope`] for more details.
+ ///
+ /// Userdata created with this method will not be able to be mutated from Lua.
+ pub fn create_any_userdata_ref<T>(&self, data: &'scope T) -> Result<AnyUserData<'lua>>
+ where
+ T: 'static,
+ {
+ unsafe {
+ let ud = self.lua.make_any_userdata(UserDataCell::new_ref(data))?;
+ self.seal_userdata::<T>(&ud)?;
+ Ok(ud)
+ }
+ }
+ /// Creates a Lua userdata object from a mutable reference to custom Rust type.
+ ///
+ /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on
+ /// scope drop, and does not require that the Rust type be Send. This method takes non-'static
+ /// mutable reference to the data. See [`Lua::scope`] for more details.
+ pub fn create_any_userdata_ref_mut<T>(&self, data: &'scope mut T) -> Result<AnyUserData<'lua>>
+ where
+ T: 'static,
+ {
+ let lua = self.lua;
+ unsafe {
+ let ud = lua.make_any_userdata(UserDataCell::new_ref_mut(data))?;
+ self.seal_userdata::<T>(&ud)?;
Ok(ud)
}
}
- /// Create a Lua userdata object from a custom userdata type.
+ /// Shortens the lifetime of a userdata to the lifetime of the scope.
+ unsafe fn seal_userdata<T: 'static>(&self, ud: &AnyUserData<'lua>) -> Result<()> {
+ #[cfg(any(feature = "lua51", feature = "luajit"))]
+ let newtable = self.lua.create_table()?;
+ let destructor: DestructorCallback = Box::new(move |ud| {
+ let state = ud.lua.state();
+ let _sg = StackGuard::new(state);
+ assert_stack(state, 2);
+
+ // Check that userdata is not destructed (via `take()` call)
+ if ud.lua.push_userdata_ref(&ud).is_err() {
+ return vec![];
+ }
+
+ // Clear associated user values
+ #[cfg(feature = "lua54")]
+ for i in 1..=USER_VALUE_MAXSLOT {
+ ffi::lua_pushnil(state);
+ ffi::lua_setiuservalue(state, -2, i as c_int);
+ }
+ #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))]
+ {
+ ffi::lua_pushnil(state);
+ ffi::lua_setuservalue(state, -2);
+ }
+ #[cfg(any(feature = "lua51", feature = "luajit"))]
+ {
+ ud.lua.push_ref(&newtable.0);
+ ffi::lua_setuservalue(state, -2);
+ }
+
+ vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
+ });
+ self.destructors
+ .borrow_mut()
+ .push((ud.0.clone(), destructor));
+
+ Ok(())
+ }
+
+ /// Creates a Lua userdata object from a custom userdata type.
///
/// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on
/// scope drop, and does not require that the userdata type be Send or 'static. See
@@ -253,10 +323,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
/// [`UserDataMethods`]: crate::UserDataMethods
pub fn create_nonstatic_userdata<T>(&self, data: T) -> Result<AnyUserData<'lua>>
where
- T: 'scope + UserData,
+ T: UserData + 'scope,
{
- 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
// 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua
@@ -264,8 +332,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// parameters.
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
scope: &Scope<'lua, 'scope>,
- data: Rc<RefCell<T>>,
- ud_ptr: *const c_void,
+ ud_ptr: *const UserDataCell<T>,
method: NonStaticMethod<'callback, T>,
) -> Result<Function<'lua>> {
// On methods that actually receive the userdata, we fake a type check on the passed in
@@ -275,7 +342,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// with a type mismatch, but here without this check would proceed as though you had
// called the method on the original value (since we otherwise completely ignore the
// first argument).
- let check_ud_type = move |lua: &'callback Lua, value| {
+ let check_ud_type = move |lua: &Lua, value| -> Result<&UserDataCell<T>> {
if let Some(Value::UserData(ud)) = value {
let state = lua.state();
unsafe {
@@ -283,7 +350,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
check_stack(state, 2)?;
lua.push_userdata_ref(&ud.0)?;
if get_userdata(state, -1) as *const _ == ud_ptr {
- return Ok(());
+ return Ok(&*ud_ptr);
}
}
};
@@ -293,8 +360,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
match method {
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_err(|_| Error::UserDataBorrowError)?;
+ let data = check_ud_type(lua, args.pop_front())?;
+ let data = data.try_borrow()?;
method(lua, &*data, args)
});
unsafe { scope.create_callback(f) }
@@ -302,13 +369,11 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
NonStaticMethod::MethodMut(method) => {
let method = RefCell::new(method);
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
- check_ud_type(lua, args.pop_front())?;
+ let data = check_ud_type(lua, args.pop_front())?;
let mut method = method
.try_borrow_mut()
.map_err(|_| Error::RecursiveMutCallback)?;
- let mut data = data
- .try_borrow_mut()
- .map_err(|_| Error::UserDataBorrowMutError)?;
+ let mut data = data.try_borrow_mut()?;
(*method)(lua, &mut *data, args)
});
unsafe { scope.create_callback(f) }
@@ -342,8 +407,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
#[cfg(not(feature = "luau"))]
#[allow(clippy::let_and_return)]
let ud_ptr = protect_lua!(state, 0, 1, |state| {
- let ud =
- ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<Rc<RefCell<T>>>>());
+ let ud = ffi::lua_newuserdata(state, mem::size_of::<UserDataCell<T>>());
// Set empty environment for Lua 5.1
#[cfg(any(feature = "lua51", feature = "luajit"))]
@@ -352,16 +416,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
ffi::lua_setuservalue(state, -2);
}
- ud
+ ud as *const UserDataCell<T>
})?;
#[cfg(feature = "luau")]
let ud_ptr = {
- crate::util::push_userdata::<UserDataCell<Rc<RefCell<T>>>>(
- state,
- UserDataCell::new(data.clone()),
- true,
- )?;
- ffi::lua_touserdata(state, -1)
+ crate::util::push_userdata(state, UserDataCell::new(data), true)?;
+ ffi::lua_touserdata(state, -1) as *const UserDataCell<T>
};
// Prepare metatable, add meta methods first and then meta fields
@@ -369,8 +429,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
push_table(state, 0, meta_methods_nrec as c_int, true)?;
for (k, m) in ud_methods.meta_methods {
- let data = data.clone();
- lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
+ lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, MetaMethod::validate(&k)?)?;
}
for (k, f) in ud_fields.meta_fields {
@@ -384,8 +443,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if field_getters_nrec > 0 {
push_table(state, 0, field_getters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_getters {
- let data = data.clone();
- lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
+ lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
}
field_getters_index = Some(ffi::lua_absindex(state, -1));
@@ -396,8 +454,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
if field_setters_nrec > 0 {
push_table(state, 0, field_setters_nrec as c_int, true)?;
for (k, m) in ud_fields.field_setters {
- let data = data.clone();
- lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
+ lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
}
field_setters_index = Some(ffi::lua_absindex(state, -1));
@@ -409,14 +466,13 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
// Create table used for methods lookup
push_table(state, 0, methods_nrec as c_int, true)?;
for (k, m) in ud_methods.methods {
- let data = data.clone();
- lua.push_value(Value::Function(wrap_method(self, data, ud_ptr, m)?))?;
+ lua.push_value(Value::Function(wrap_method(self, ud_ptr, m)?))?;
rawset_field(state, -2, &k)?;
}
methods_index = Some(ffi::lua_absindex(state, -1));
}
- init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
+ init_userdata_metatable::<UserDataCell<T>>(
state,
metatable_index,
field_getters_index,
@@ -478,8 +534,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
mem::transmute(f)
}
- let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
- vec![ud]
+ let ud = take_userdata::<UserDataCell<T>>(state);
+ vec![Box::new(seal(ud))]
});
self.destructors
.borrow_mut()
diff --git a/src/userdata.rs b/src/userdata.rs
index ca28b53..bcc735a 100644
--- a/src/userdata.rs
+++ b/src/userdata.rs
@@ -578,12 +578,22 @@ pub trait UserData: Sized {
}
// Wraps UserData in a way to always implement `serde::Serialize` trait.
-pub(crate) struct UserDataCell<T>(RefCell<UserDataWrapped<T>>);
+pub(crate) struct UserDataCell<T>(RefCell<UserDataVariant<T>>);
impl<T> UserDataCell<T> {
#[inline]
pub(crate) fn new(data: T) -> Self {
- UserDataCell(RefCell::new(UserDataWrapped::new(data)))
+ UserDataCell(RefCell::new(UserDataVariant::new(data)))
+ }
+
+ #[inline]
+ pub(crate) fn new_ref(data: &T) -> Self {
+ UserDataCell(RefCell::new(UserDataVariant::new_ref(data)))
+ }
+
+ #[inline]
+ pub(crate) fn new_ref_mut(data: &mut T) -> Self {
+ UserDataCell(RefCell::new(UserDataVariant::new_ref_mut(data)))
}
#[cfg(feature = "serialize")]
@@ -592,7 +602,7 @@ impl<T> UserDataCell<T> {
where
T: Serialize + 'static,
{
- UserDataCell(RefCell::new(UserDataWrapped::new_ser(data)))
+ UserDataCell(RefCell::new(UserDataVariant::new_ser(data)))
}
// Immutably borrows the wrapped value.
@@ -609,27 +619,42 @@ impl<T> UserDataCell<T> {
pub(crate) fn try_borrow_mut(&self) -> Result<RefMut<T>> {
self.0
.try_borrow_mut()
- .map(|r| RefMut::map(r, |r| r.deref_mut()))
.map_err(|_| Error::UserDataBorrowMutError)
+ .and_then(|r| {
+ RefMut::filter_map(r, |r| r.try_deref_mut().ok())
+ .map_err(|_| Error::UserDataBorrowMutError)
+ })
}
// Consumes this `UserDataCell`, returning the wrapped value.
#[inline]
- unsafe fn into_inner(self) -> T {
+ fn into_inner(self) -> Result<T> {
self.0.into_inner().into_inner()
}
}
-pub(crate) enum UserDataWrapped<T> {
+pub(crate) enum UserDataVariant<T> {
Default(Box<T>),
+ Ref(*const T),
+ RefMut(*mut T),
#[cfg(feature = "serialize")]
Serializable(Box<dyn erased_serde::Serialize>),
}
-impl<T> UserDataWrapped<T> {
+impl<T> UserDataVariant<T> {
#[inline]
fn new(data: T) -> Self {
- UserDataWrapped::Default(Box::new(data))
+ UserDataVariant::Default(Box::new(data))
+ }
+
+ #[inline]
+ fn new_ref(data: &T) -> Self {
+ UserDataVariant::Ref(data)
+ }
+
+ #[inline]
+ fn new_ref_mut(data: &mut T) -> Self {
+ UserDataVariant::RefMut(data)
}
#[cfg(feature = "serialize")]
@@ -638,42 +663,45 @@ impl<T> UserDataWrapped<T> {
where
T: Serialize + 'static,
{
- UserDataWrapped::Serializable(Box::new(data))
+ UserDataVariant::Serializable(Box::new(data))
}
#[inline]
- unsafe fn into_inner(self) -> T {
+ fn try_deref_mut(&mut self) -> Result<&mut T> {
match self {
- Self::Default(data) => *data,
+ Self::Default(data) => Ok(data.deref_mut()),
+ Self::Ref(_) => Err(Error::UserDataBorrowMutError),
+ Self::RefMut(data) => unsafe { Ok(&mut **data) },
#[cfg(feature = "serialize")]
- Self::Serializable(data) => *Box::from_raw(Box::into_raw(data) as *mut T),
+ Self::Serializable(data) => unsafe { Ok(&mut *(data.as_mut() as *mut _ as *mut T)) },
}
}
-}
-
-impl<T> Deref for UserDataWrapped<T> {
- type Target = T;
#[inline]
- fn deref(&self) -> &Self::Target {
+ fn into_inner(self) -> Result<T> {
match self {
- Self::Default(data) => data,
+ Self::Default(data) => Ok(*data),
+ Self::Ref(_) | Self::RefMut(_) => Err(Error::UserDataTypeMismatch),
#[cfg(feature = "serialize")]
Self::Serializable(data) => unsafe {
- &*(data.as_ref() as *const _ as *const Self::Target)
+ Ok(*Box::from_raw(Box::into_raw(data) as *mut T))
},
}
}
}
-impl<T> DerefMut for UserDataWrapped<T> {
+impl<T> Deref for UserDataVariant<T> {
+ type Target = T;
+
#[inline]
- fn deref_mut(&mut self) -> &mut Self::Target {
+ fn deref(&self) -> &Self::Target {
match self {
Self::Default(data) => data,
+ Self::Ref(data) => unsafe { &**data },
+ Self::RefMut(data) => unsafe { &**data },
#[cfg(feature = "serialize")]
Self::Serializable(data) => unsafe {
- &mut *(data.as_mut() as *mut _ as *mut Self::Target)
+ &*(data.as_ref() as *const _ as *const Self::Target)
},
}
}
@@ -771,7 +799,7 @@ impl<'lua> AnyUserData<'lua> {
Some(type_id) if type_id == TypeId::of::<T>() => {
// Try to borrow userdata exclusively
let _ = (*get_userdata::<UserDataCell<T>>(state, -1)).try_borrow_mut()?;
- Ok(take_userdata::<UserDataCell<T>>(state).into_inner())
+ take_userdata::<UserDataCell<T>>(state).into_inner()
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -1043,8 +1071,8 @@ impl<'lua> AnyUserData<'lua> {
let ud = &*get_userdata::<UserDataCell<()>>(state, -1);
match &*ud.0.try_borrow().map_err(|_| Error::UserDataBorrowError)? {
- UserDataWrapped::Default(_) => Result::Ok(false),
- UserDataWrapped::Serializable(_) => Result::Ok(true),
+ UserDataVariant::Serializable(_) => Result::Ok(true),
+ _ => Result::Ok(false),
}
};
is_serializable().unwrap_or(false)
@@ -1184,8 +1212,8 @@ impl<'lua> Serialize for AnyUserData<'lua> {
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
};
match &*data {
- UserDataWrapped::Default(_) => UserDataSerializeError.serialize(serializer),
- UserDataWrapped::Serializable(ser) => ser.serialize(serializer),
+ UserDataVariant::Serializable(ser) => ser.serialize(serializer),
+ _ => UserDataSerializeError.serialize(serializer),
}
}
}
diff --git a/tests/scope.rs b/tests/scope.rs
index 9640383..4fde34a 100644
--- a/tests/scope.rs
+++ b/tests/scope.rs
@@ -356,3 +356,107 @@ fn test_scope_nonstatic_userdata_drop() -> Result<()> {
Ok(())
}
+
+#[test]
+fn test_scope_userdata_ref() -> Result<()> {
+ let lua = Lua::new();
+
+ struct MyUserData(Cell<i64>);
+
+ impl UserData for MyUserData {
+ fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_method("inc", |_, data, ()| {
+ data.0.set(data.0.get() + 1);
+ Ok(())
+ });
+
+ methods.add_method("dec", |_, data, ()| {
+ data.0.set(data.0.get() - 1);
+ Ok(())
+ });
+ }
+ }
+
+ let data = MyUserData(Cell::new(1));
+ lua.scope(|scope| {
+ let ud = scope.create_userdata_ref(&data)?;
+ modify_userdata(&lua, ud)
+ })?;
+ assert_eq!(data.0.get(), 2);
+
+ Ok(())
+}
+
+#[test]
+fn test_scope_userdata_ref_mut() -> Result<()> {
+ let lua = Lua::new();
+
+ struct MyUserData(i64);
+
+ impl UserData for MyUserData {
+ fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
+ methods.add_method_mut("inc", |_, data, ()| {
+ data.0 += 1;
+ Ok(())
+ });
+
+ methods.add_method_mut("dec", |_, data, ()| {
+ data.0 -= 1;
+ Ok(())
+ });
+ }
+ }
+
+ let mut data = MyUserData(1);
+ lua.scope(|scope| {
+ let ud = scope.create_userdata_ref_mut(&mut data)?;
+ modify_userdata(&lua, ud)
+ })?;
+ assert_eq!(data.0, 2);
+
+ Ok(())
+}
+
+#[test]
+fn test_scope_any_userdata_ref() -> Result<()> {
+ let lua = Lua::new();
+
+ lua.register_userdata_type::<Cell<i64>>(|reg| {
+ reg.add_method("inc", |_, data, ()| {
+ data.set(data.get() + 1);
+ Ok(())
+ });
+
+ reg.add_method("dec", |_, data, ()| {
+ data.set(data.get() - 1);
+ Ok(())
+ });
+ })?;
+
+ let data = Cell::new(1i64);
+ lua.scope(|scope| {
+ let ud = scope.create_any_userdata_ref(&data)?;
+ modify_userdata(&lua, ud)
+ })?;
+ assert_eq!(data.get(), 2);
+
+ Ok(())
+}
+
+fn modify_userdata(lua: &Lua, ud: AnyUserData) -> Result<()> {
+ let f: Function = lua
+ .load(
+ r#"
+ function(u)
+ u:inc()
+ u:dec()
+ u:inc()
+ end
+"#,
+ )
+ .eval()?;
+
+ f.call(ud)?;
+
+ Ok(())
+}