Add create_userdata_ref/create_userdata_ref_mut for scope (#206)
New methods would allow creating userdata objects from (mutable) reference to a UserData of registered type.
This commit is contained in:
parent
b790b525c1
commit
f52abf919e
|
@ -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"))]
|
||||
|
|
42
src/lua.rs
42
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>,
|
||||
|
|
|
@ -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"))]
|
||||
|
|
234
src/scope.rs
234
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))
|
||||
}
|
||||
|
||||
fn create_userdata_inner<T>(&self, data: UserDataCell<T>) -> Result<AnyUserData<'lua>>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
{
|
||||
// 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![];
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
let ud = self.lua.make_userdata(UserDataCell::new_ser(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a Lua userdata object from a custom userdata type.
|
||||
/// 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: UserData + 'static,
|
||||
{
|
||||
unsafe {
|
||||
let ud = self.lua.make_userdata(UserDataCell::new_ref(data))?;
|
||||
self.seal_userdata::<T>(&ud)?;
|
||||
Ok(ud)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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()
|
||||
|
|
|
@ -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,26 +663,42 @@ 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)) },
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn into_inner(self) -> Result<T> {
|
||||
match self {
|
||||
Self::Default(data) => Ok(*data),
|
||||
Self::Ref(_) | Self::RefMut(_) => Err(Error::UserDataTypeMismatch),
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data) => unsafe {
|
||||
Ok(*Box::from_raw(Box::into_raw(data) as *mut T))
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataWrapped<T> {
|
||||
impl<T> Deref for UserDataVariant<T> {
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
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 {
|
||||
&*(data.as_ref() as *const _ as *const Self::Target)
|
||||
|
@ -666,19 +707,6 @@ impl<T> Deref for UserDataWrapped<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataWrapped<T> {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match self {
|
||||
Self::Default(data) => data,
|
||||
#[cfg(feature = "serialize")]
|
||||
Self::Serializable(data) => unsafe {
|
||||
&mut *(data.as_mut() as *mut _ as *mut Self::Target)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
struct UserDataSerializeError;
|
||||
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
104
tests/scope.rs
104
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(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue