Implement `UserData` for Rc<RefCell>/Arc<Mutex>/Arc<RwLock> wrappers
This commit is contained in:
parent
bae424672a
commit
a944f4ad6f
128
src/lua.rs
128
src/lua.rs
|
@ -6,7 +6,7 @@ use std::fmt;
|
|||
use std::marker::PhantomData;
|
||||
use std::os::raw::{c_char, c_int, c_void};
|
||||
use std::panic::resume_unwind;
|
||||
use std::sync::{Arc, Mutex, MutexGuard, Weak};
|
||||
use std::sync::{Arc, Mutex, MutexGuard, RwLock, Weak};
|
||||
use std::{mem, ptr, str};
|
||||
|
||||
use crate::error::{Error, Result};
|
||||
|
@ -32,6 +32,9 @@ use crate::util::{
|
|||
};
|
||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use {
|
||||
crate::types::AsyncCallback,
|
||||
|
@ -1607,6 +1610,11 @@ impl Lua {
|
|||
self.push_value(f(self)?)?;
|
||||
ffi::safe::lua_rawsetfield(self.state, -2, k.validate()?.name())?;
|
||||
}
|
||||
// Add special `__mlua_type_id` field
|
||||
let type_id_ptr =
|
||||
ffi::safe::lua_newuserdata(self.state, mem::size_of::<TypeId>())? as *mut TypeId;
|
||||
ptr::write(type_id_ptr, type_id);
|
||||
ffi::safe::lua_rawsetfield(self.state, -2, "__mlua_type_id")?;
|
||||
let metatable_index = ffi::lua_absindex(self.state, -1);
|
||||
|
||||
let mut extra_tables_count = 0;
|
||||
|
@ -1690,7 +1698,7 @@ impl Lua {
|
|||
// Pushes a LuaRef value onto the stack, checking that it's a registered
|
||||
// and not destructed UserData.
|
||||
// Uses 3 stack spaces, does not call checkstack.
|
||||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef) -> Result<()> {
|
||||
pub(crate) unsafe fn push_userdata_ref(&self, lref: &LuaRef, with_mt: bool) -> Result<()> {
|
||||
self.push_ref(lref);
|
||||
if ffi::lua_getmetatable(self.state, -1) == 0 {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
|
@ -1699,7 +1707,9 @@ impl Lua {
|
|||
let ptr = ffi::lua_topointer(self.state, -1);
|
||||
let extra = mlua_expect!(self.extra.lock(), "extra is poisoned");
|
||||
if extra.registered_userdata_mt.contains(&(ptr as isize)) {
|
||||
if !with_mt {
|
||||
ffi::lua_pop(self.state, 1);
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
// Maybe userdata was destructed?
|
||||
|
@ -2488,6 +2498,21 @@ impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMet
|
|||
self.meta_methods
|
||||
.push((meta.into(), Self::box_function_mut(function)));
|
||||
}
|
||||
|
||||
// Below are internal methods used in generated code
|
||||
|
||||
fn add_callback(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
self.methods.push((name, callback));
|
||||
}
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, name: Vec<u8>, callback: AsyncCallback<'lua, 'static>) {
|
||||
self.async_methods.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_meta_callback(&mut self, meta: MetaMethod, callback: Callback<'lua, 'static>) {
|
||||
self.meta_methods.push((meta, callback));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
||||
|
@ -2500,8 +2525,29 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
|||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
let userdata = userdata.borrow::<T>()?;
|
||||
method(lua, &userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
match userdata.type_id()? {
|
||||
id if id == TypeId::of::<T>() => {
|
||||
let ud = userdata.borrow::<T>()?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
id if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = userdata.borrow::<Rc<RefCell<T>>>()?;
|
||||
let ud = ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<Mutex<T>>>()?;
|
||||
let ud = ud.try_lock().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<RwLock<T>>>()?;
|
||||
let ud = ud.try_read().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
|
@ -2522,11 +2568,34 @@ impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> {
|
|||
Box::new(move |lua, mut args| {
|
||||
if let Some(front) = args.pop_front() {
|
||||
let userdata = AnyUserData::from_lua(front, lua)?;
|
||||
let mut userdata = userdata.borrow_mut::<T>()?;
|
||||
let mut method = method
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
(&mut *method)(lua, &mut userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
match userdata.type_id()? {
|
||||
id if id == TypeId::of::<T>() => {
|
||||
let mut ud = userdata.borrow_mut::<T>()?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
#[cfg(not(feature = "send"))]
|
||||
id if id == TypeId::of::<Rc<RefCell<T>>>() => {
|
||||
let ud = userdata.borrow::<Rc<RefCell<T>>>()?;
|
||||
let mut ud = ud
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<Mutex<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<Mutex<T>>>()?;
|
||||
let mut ud = ud.try_lock().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
id if id == TypeId::of::<Arc<RwLock<T>>>() => {
|
||||
let ud = userdata.borrow::<Arc<RwLock<T>>>()?;
|
||||
let mut ud = ud.try_write().map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
method(lua, &mut ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)
|
||||
}
|
||||
_ => Err(Error::UserDataTypeMismatch),
|
||||
}
|
||||
} else {
|
||||
Err(Error::FromLuaConversionError {
|
||||
from: "missing argument",
|
||||
|
@ -2709,4 +2778,51 @@ impl<'lua, T: 'static + UserData> UserDataFields<'lua, T> for StaticUserDataFiel
|
|||
}),
|
||||
));
|
||||
}
|
||||
|
||||
// Below are internal methods
|
||||
|
||||
fn add_field_getter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
self.field_getters.push((name, callback));
|
||||
}
|
||||
|
||||
fn add_field_setter(&mut self, name: Vec<u8>, callback: Callback<'lua, 'static>) {
|
||||
self.field_setters.push((name, callback));
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! lua_userdata_impl {
|
||||
($type:ty) => {
|
||||
impl<T: 'static + UserData> UserData for $type {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
let mut orig_fields = StaticUserDataFields::default();
|
||||
T::add_fields(&mut orig_fields);
|
||||
for (name, callback) in orig_fields.field_getters {
|
||||
fields.add_field_getter(name, callback);
|
||||
}
|
||||
for (name, callback) in orig_fields.field_setters {
|
||||
fields.add_field_setter(name, callback);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||
let mut orig_methods = StaticUserDataMethods::default();
|
||||
T::add_methods(&mut orig_methods);
|
||||
for (name, callback) in orig_methods.methods {
|
||||
methods.add_callback(name, callback);
|
||||
}
|
||||
#[cfg(feature = "async")]
|
||||
for (name, callback) in orig_methods.async_methods {
|
||||
methods.add_async_callback(name, callback);
|
||||
}
|
||||
for (meta, callback) in orig_methods.meta_methods {
|
||||
methods.add_meta_callback(meta, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
lua_userdata_impl!(Rc<RefCell<T>>);
|
||||
lua_userdata_impl!(Arc<Mutex<T>>);
|
||||
lua_userdata_impl!(Arc<RwLock<T>>);
|
||||
|
|
22
src/scope.rs
22
src/scope.rs
|
@ -1,8 +1,9 @@
|
|||
use std::any::Any;
|
||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::os::raw::{c_int, c_void};
|
||||
use std::rc::Rc;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::Serialize;
|
||||
|
@ -238,7 +239,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
where
|
||||
T: 'scope + UserData,
|
||||
{
|
||||
let data = UserDataCell::new_arc(data);
|
||||
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
|
||||
|
@ -247,7 +248,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
// parameters.
|
||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||
scope: &Scope<'lua, 'scope>,
|
||||
data: UserDataCell<T>,
|
||||
data: Rc<RefCell<T>>,
|
||||
data_ptr: *mut c_void,
|
||||
method: NonStaticMethod<'callback, T>,
|
||||
) -> Result<Function<'lua>> {
|
||||
|
@ -263,7 +264,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
lua.push_userdata_ref(&ud.0)?;
|
||||
lua.push_userdata_ref(&ud.0, false)?;
|
||||
if get_userdata(lua.state, -1) == data_ptr {
|
||||
return Ok(());
|
||||
}
|
||||
|
@ -276,10 +277,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
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(|cell| Ref::map(cell, AsRef::as_ref))
|
||||
.map_err(|_| Error::UserDataBorrowError)?;
|
||||
let data = data.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
method(lua, &*data, args)
|
||||
});
|
||||
unsafe { scope.create_callback(f) }
|
||||
|
@ -293,7 +291,6 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||
let mut data = data
|
||||
.try_borrow_mut()
|
||||
.map(|cell| RefMut::map(cell, AsMut::as_mut))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
(&mut *method)(lua, &mut *data, args)
|
||||
});
|
||||
|
@ -324,7 +321,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 13)?;
|
||||
|
||||
push_userdata(lua.state, data.clone())?;
|
||||
push_userdata(lua.state, UserDataCell::new(data.clone()))?;
|
||||
let data_ptr = ffi::lua_touserdata(lua.state, -1);
|
||||
|
||||
// Prepare metatable, add meta methods first and then meta fields
|
||||
|
@ -379,7 +376,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
methods_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||
}
|
||||
|
||||
init_userdata_metatable::<()>(
|
||||
init_userdata_metatable::<UserDataCell<Rc<RefCell<T>>>>(
|
||||
lua.state,
|
||||
metatable_index,
|
||||
field_getters_index,
|
||||
|
@ -427,7 +424,8 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
|||
mem::transmute(f)
|
||||
}
|
||||
|
||||
vec![Box::new(seal(take_userdata::<UserDataCell<T>>(state)))]
|
||||
let ud = Box::new(seal(take_userdata::<UserDataCell<Rc<RefCell<T>>>>(state)));
|
||||
vec![ud]
|
||||
});
|
||||
self.destructors
|
||||
.borrow_mut()
|
||||
|
|
248
src/userdata.rs
248
src/userdata.rs
|
@ -1,9 +1,9 @@
|
|||
use std::any::TypeId;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
use std::ops::Deref;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::string::String as StdString;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use std::future::Future;
|
||||
|
@ -19,13 +19,16 @@ use crate::ffi;
|
|||
use crate::function::Function;
|
||||
use crate::lua::Lua;
|
||||
use crate::table::{Table, TablePairs};
|
||||
use crate::types::{LuaRef, MaybeSend};
|
||||
use crate::types::{Callback, LuaRef, MaybeSend};
|
||||
use crate::util::{check_stack, get_destructed_userdata_metatable, get_userdata, StackGuard};
|
||||
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
|
||||
|
||||
#[cfg(any(feature = "lua52", feature = "lua51", feature = "luajit"))]
|
||||
use crate::value::Value;
|
||||
|
||||
#[cfg(feature = "async")]
|
||||
use crate::types::AsyncCallback;
|
||||
|
||||
/// Kinds of metamethods that can be overridden.
|
||||
///
|
||||
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
|
||||
|
@ -403,6 +406,20 @@ pub trait UserDataMethods<'lua, T: UserData> {
|
|||
A: FromLuaMulti<'lua>,
|
||||
R: ToLuaMulti<'lua>,
|
||||
F: 'static + MaybeSend + FnMut(&'lua Lua, A) -> Result<R>;
|
||||
|
||||
//
|
||||
// Below are internal methods used in generated code
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_callback(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[cfg(feature = "async")]
|
||||
fn add_async_callback(&mut self, _name: Vec<u8>, _callback: AsyncCallback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_meta_callback(&mut self, _meta: MetaMethod, _callback: Callback<'lua, 'static>) {}
|
||||
}
|
||||
|
||||
/// Field registry for [`UserData`] implementors.
|
||||
|
@ -474,6 +491,16 @@ pub trait UserDataFields<'lua, T: UserData> {
|
|||
S: Into<MetaMethod>,
|
||||
F: 'static + MaybeSend + Fn(&'lua Lua) -> Result<R>,
|
||||
R: ToLua<'lua>;
|
||||
|
||||
//
|
||||
// Below are internal methods used in generated code
|
||||
//
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_getter(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
|
||||
#[doc(hidden)]
|
||||
fn add_field_setter(&mut self, _name: Vec<u8>, _callback: Callback<'lua, 'static>) {}
|
||||
}
|
||||
|
||||
/// Trait for custom userdata types.
|
||||
|
@ -550,26 +577,11 @@ pub trait UserData: Sized {
|
|||
}
|
||||
|
||||
// Wraps UserData in a way to always implement `serde::Serialize` trait.
|
||||
pub(crate) enum UserDataCell<T> {
|
||||
Arc(Arc<RefCell<UserDataWrapped<T>>>),
|
||||
Plain(RefCell<UserDataWrapped<T>>),
|
||||
}
|
||||
pub(crate) struct UserDataCell<T>(RefCell<UserDataWrapped<T>>);
|
||||
|
||||
impl<T> UserDataCell<T> {
|
||||
pub(crate) fn new(data: T) -> Self {
|
||||
UserDataCell::Plain(RefCell::new(UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn new_arc(data: T) -> Self {
|
||||
UserDataCell::Arc(Arc::new(RefCell::new(UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
})))
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new(data)))
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
|
@ -577,40 +589,135 @@ impl<T> UserDataCell<T> {
|
|||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataCell::Plain(RefCell::new(UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}))
|
||||
UserDataCell(RefCell::new(UserDataWrapped::new_ser(data)))
|
||||
}
|
||||
|
||||
// Immutably borrows the wrapped value.
|
||||
fn try_borrow(&self) -> Result<UserDataRef<T>> {
|
||||
self.0
|
||||
.try_borrow()
|
||||
.map(|r| UserDataRef(UserDataRefInner::Ref(r)))
|
||||
.map_err(|_| Error::UserDataBorrowError)
|
||||
}
|
||||
|
||||
// Mutably borrows the wrapped value.
|
||||
fn try_borrow_mut(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.0
|
||||
.try_borrow_mut()
|
||||
.map(|r| UserDataRefMut(UserDataRefMutInner::Ref(r)))
|
||||
.map_err(|_| Error::UserDataBorrowMutError)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataCell<T> {
|
||||
type Target = RefCell<UserDataWrapped<T>>;
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataCell<()> {
|
||||
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let ser = self
|
||||
.0
|
||||
.try_borrow()
|
||||
.map_err(|_| ser::Error::custom(Error::UserDataBorrowError))?
|
||||
.ser;
|
||||
unsafe { (&*ser).serialize(serializer) }
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type for an immutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRef<'a, T>(UserDataRefInner<'a, T>);
|
||||
|
||||
enum UserDataRefInner<'a, T> {
|
||||
Ref(Ref<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
/// A wrapper type for a mutably borrowed value from an `AnyUserData`.
|
||||
pub struct UserDataRefMut<'a, T>(UserDataRefMutInner<'a, T>);
|
||||
|
||||
enum UserDataRefMutInner<'a, T> {
|
||||
Ref(RefMut<'a, UserDataWrapped<T>>),
|
||||
}
|
||||
|
||||
impl<T> Deref for UserDataRef<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
UserDataCell::Arc(t) => &*t,
|
||||
UserDataCell::Plain(t) => &*t,
|
||||
match &self.0 {
|
||||
UserDataRefInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for UserDataCell<T> {
|
||||
fn clone(&self) -> Self {
|
||||
match self {
|
||||
UserDataCell::Arc(t) => UserDataCell::Arc(t.clone()),
|
||||
UserDataCell::Plain(_) => mlua_panic!("cannot clone non-arc userdata"),
|
||||
impl<T> Deref for UserDataRefMut<'_, T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match &self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &*x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> DerefMut for UserDataRefMut<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
match &mut self.0 {
|
||||
UserDataRefMutInner::Ref(x) => &mut *x,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Debug> fmt::Debug for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Debug::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRef<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: fmt::Display> fmt::Display for UserDataRefMut<'_, T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
fmt::Display::fmt(&*self as &T, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct UserDataWrapped<T> {
|
||||
pub(crate) data: *mut T,
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: *mut dyn erased_serde::Serialize,
|
||||
}
|
||||
|
||||
impl<T> UserDataWrapped<T> {
|
||||
fn new(data: T) -> Self {
|
||||
UserDataWrapped {
|
||||
data: Box::into_raw(Box::new(data)),
|
||||
#[cfg(feature = "serialize")]
|
||||
ser: Box::into_raw(Box::new(UserDataSerializeError)),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
fn new_ser(data: T) -> Self
|
||||
where
|
||||
T: 'static + Serialize,
|
||||
{
|
||||
let data_raw = Box::into_raw(Box::new(data));
|
||||
UserDataWrapped {
|
||||
data: data_raw,
|
||||
ser: data_raw,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Drop for UserDataWrapped<T> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
|
@ -623,20 +730,22 @@ impl<T> Drop for UserDataWrapped<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for UserDataWrapped<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
impl<T> Deref for UserDataWrapped<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*self.data }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsMut<T> for UserDataWrapped<T> {
|
||||
fn as_mut(&mut self) -> &mut T {
|
||||
impl<T> DerefMut for UserDataWrapped<T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
unsafe { &mut *self.data }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
pub(crate) struct UserDataSerializeError;
|
||||
struct UserDataSerializeError;
|
||||
|
||||
#[cfg(feature = "serialize")]
|
||||
impl Serialize for UserDataSerializeError {
|
||||
|
@ -683,26 +792,18 @@ impl<'lua> AnyUserData<'lua> {
|
|||
///
|
||||
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<Ref<T>> {
|
||||
self.inspect(|cell| {
|
||||
let cell_ref = cell.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
|
||||
Ok(Ref::map(cell_ref, |x| unsafe { &*x.data }))
|
||||
})
|
||||
pub fn borrow<T: 'static + UserData>(&self) -> Result<UserDataRef<T>> {
|
||||
self.inspect(|cell| cell.try_borrow())
|
||||
}
|
||||
|
||||
/// Borrow this userdata mutably if it is of type `T`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata is already borrowed. Returns a
|
||||
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<RefMut<T>> {
|
||||
self.inspect(|cell| {
|
||||
let cell_ref = cell
|
||||
.try_borrow_mut()
|
||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||
Ok(RefMut::map(cell_ref, |x| unsafe { &mut *x.data }))
|
||||
})
|
||||
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
|
||||
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
|
||||
pub fn borrow_mut<T: 'static + UserData>(&self) -> Result<UserDataRefMut<T>> {
|
||||
self.inspect(|cell| cell.try_borrow_mut())
|
||||
}
|
||||
|
||||
/// Sets an associated value to this `AnyUserData`.
|
||||
|
@ -726,7 +827,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
lua.push_value(v)?;
|
||||
ffi::lua_setuservalue(lua.state, -2);
|
||||
|
||||
|
@ -745,7 +846,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
ffi::lua_getuservalue(lua.state, -1);
|
||||
lua.pop_value()
|
||||
};
|
||||
|
@ -776,7 +877,7 @@ impl<'lua> AnyUserData<'lua> {
|
|||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)?;
|
||||
ffi::lua_getmetatable(lua.state, -1); // Checked that non-empty on the previous call
|
||||
Ok(Table(lua.pop_ref()))
|
||||
}
|
||||
|
@ -803,6 +904,25 @@ impl<'lua> AnyUserData<'lua> {
|
|||
Ok(false)
|
||||
}
|
||||
|
||||
pub(crate) fn type_id(&self) -> Result<TypeId> {
|
||||
let lua = self.0.lua;
|
||||
unsafe {
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 5)?;
|
||||
|
||||
// Push userdata with metatable
|
||||
lua.push_userdata_ref(&self.0, true)?;
|
||||
|
||||
// Get the special `__mlua_type_id`
|
||||
ffi::safe::lua_pushstring(lua.state, "__mlua_type_id")?;
|
||||
if ffi::lua_rawget(lua.state, -2) != ffi::LUA_TUSERDATA {
|
||||
return Err(Error::UserDataTypeMismatch);
|
||||
}
|
||||
|
||||
Ok(*(ffi::lua_touserdata(lua.state, -1) as *const TypeId))
|
||||
}
|
||||
}
|
||||
|
||||
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
|
||||
where
|
||||
T: 'static + UserData,
|
||||
|
@ -928,17 +1048,15 @@ impl<'lua> Serialize for AnyUserData<'lua> {
|
|||
where
|
||||
S: Serializer,
|
||||
{
|
||||
let res = (|| unsafe {
|
||||
unsafe {
|
||||
let lua = self.0.lua;
|
||||
let _sg = StackGuard::new(lua.state);
|
||||
check_stack(lua.state, 3)?;
|
||||
check_stack(lua.state, 3).map_err(ser::Error::custom)?;
|
||||
|
||||
lua.push_userdata_ref(&self.0)?;
|
||||
lua.push_userdata_ref(&self.0, false)
|
||||
.map_err(ser::Error::custom)?;
|
||||
let ud = &*get_userdata::<UserDataCell<()>>(lua.state, -1);
|
||||
(*ud.try_borrow().map_err(|_| Error::UserDataBorrowError)?.ser)
|
||||
.serialize(serializer)
|
||||
.map_err(|err| Error::SerializeError(err.to_string()))
|
||||
})();
|
||||
res.map_err(ser::Error::custom)
|
||||
ud.serialize(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
#[cfg(feature = "lua54")]
|
||||
use std::sync::atomic::{AtomicI64, Ordering};
|
||||
|
@ -451,3 +454,53 @@ fn test_metatable() -> Result<()> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_userdata_wrapped() -> Result<()> {
|
||||
struct MyUserData(i64);
|
||||
|
||||
impl UserData for MyUserData {
|
||||
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
|
||||
fields.add_field_method_get("data", |_, this| Ok(this.0));
|
||||
fields.add_field_method_set("data", |_, this, val| {
|
||||
this.0 = val;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let lua = Lua::new();
|
||||
let globals = lua.globals();
|
||||
|
||||
#[cfg(not(feature = "send"))]
|
||||
{
|
||||
globals.set("rc_refcell_ud", Rc::new(RefCell::new(MyUserData(1))))?;
|
||||
lua.load(
|
||||
r#"
|
||||
rc_refcell_ud.data = rc_refcell_ud.data + 1
|
||||
assert(rc_refcell_ud.data == 2)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
}
|
||||
|
||||
globals.set("arc_mutex_ud", Arc::new(Mutex::new(MyUserData(2))))?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_mutex_ud.data = arc_mutex_ud.data + 1
|
||||
assert(arc_mutex_ud.data == 3)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
globals.set("arc_rwlock_ud", Arc::new(RwLock::new(MyUserData(3))))?;
|
||||
lua.load(
|
||||
r#"
|
||||
arc_rwlock_ud.data = arc_rwlock_ud.data + 1
|
||||
assert(arc_rwlock_ud.data == 4)
|
||||
"#,
|
||||
)
|
||||
.exec()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue