Add Lua::scope back

This commit is contained in:
Alex Orlenko
2024-09-20 13:01:55 +01:00
parent 7c2e9b5a7c
commit da4404baa5
42 changed files with 883 additions and 1643 deletions
+1 -1
View File
@@ -87,7 +87,7 @@ mod hook;
mod luau;
mod memory;
mod multi;
// mod scope;
mod scope;
mod state;
mod stdlib;
mod string;
+112 -790
View File
File diff suppressed because it is too large Load Diff
+17 -20
View File
@@ -12,7 +12,7 @@ use crate::error::{Error, Result};
use crate::function::Function;
use crate::hook::Debug;
use crate::memory::MemoryState;
// use crate::scope::Scope;
use crate::scope::Scope;
use crate::stdlib::StdLib;
use crate::string::String;
use crate::table::Table;
@@ -21,7 +21,7 @@ use crate::types::{
AppDataRef, AppDataRefMut, ArcReentrantMutexGuard, Integer, LightUserData, MaybeSend, Number,
ReentrantMutex, ReentrantMutexGuard, RegistryKey, XRc, XWeak,
};
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataVariant};
use crate::userdata::{AnyUserData, UserData, UserDataProxy, UserDataRegistry, UserDataStorage};
use crate::util::{assert_stack, check_stack, push_string, push_table, rawset_field, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, MultiValue, Nil, Value};
@@ -1201,7 +1201,7 @@ impl Lua {
where
T: UserData + MaybeSend + 'static,
{
unsafe { self.lock().make_userdata(UserDataVariant::new(data)) }
unsafe { self.lock().make_userdata(UserDataStorage::new(data)) }
}
/// Creates a Lua userdata object from a custom serializable userdata type.
@@ -1214,7 +1214,7 @@ impl Lua {
where
T: UserData + Serialize + MaybeSend + 'static,
{
unsafe { self.lock().make_userdata(UserDataVariant::new_ser(data)) }
unsafe { self.lock().make_userdata(UserDataStorage::new_ser(data)) }
}
/// Creates a Lua userdata object from a custom Rust type.
@@ -1229,7 +1229,7 @@ impl Lua {
where
T: MaybeSend + 'static,
{
unsafe { self.lock().make_any_userdata(UserDataVariant::new(data)) }
unsafe { self.lock().make_any_userdata(UserDataStorage::new(data)) }
}
/// Creates a Lua userdata object from a custom serializable Rust type.
@@ -1244,26 +1244,26 @@ impl Lua {
where
T: Serialize + MaybeSend + 'static,
{
unsafe { (self.lock()).make_any_userdata(UserDataVariant::new_ser(data)) }
unsafe { (self.lock()).make_any_userdata(UserDataStorage::new_ser(data)) }
}
/// Registers a custom Rust type in Lua to use in userdata objects.
///
/// This methods provides a way to add fields or methods to userdata objects of a type `T`.
pub fn register_userdata_type<T: 'static>(&self, f: impl FnOnce(&mut UserDataRegistry<T>)) -> Result<()> {
let mut registry = const { UserDataRegistry::new() };
let type_id = TypeId::of::<T>();
let mut registry = UserDataRegistry::new(type_id);
f(&mut registry);
let lua = self.lock();
unsafe {
// Deregister the type if it already registered
let type_id = TypeId::of::<T>();
if let Some(&table_id) = (*lua.extra.get()).registered_userdata.get(&type_id) {
if let Some(&table_id) = (*lua.extra.get()).registered_userdata_t.get(&type_id) {
ffi::luaL_unref(lua.state(), ffi::LUA_REGISTRYINDEX, table_id);
}
// Register the type
lua.register_userdata_metatable(registry)?;
lua.create_userdata_metatable(registry)?;
}
Ok(())
}
@@ -1306,7 +1306,7 @@ impl Lua {
T: UserData + 'static,
{
let ud = UserDataProxy::<T>(PhantomData);
unsafe { self.lock().make_userdata(UserDataVariant::new(ud)) }
unsafe { self.lock().make_userdata(UserDataStorage::new(ud)) }
}
/// Sets the metatable for a Luau builtin vector type.
@@ -1380,15 +1380,12 @@ 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>(
// &'lua self,
// f: impl FnOnce(&Scope<'lua, 'scope>) -> Result<R>,
// ) -> Result<R>
// where
// 'lua: 'scope,
// {
// f(&Scope::new(self))
// }
pub fn scope<'env, R>(
&self,
f: impl for<'scope> FnOnce(&'scope mut Scope<'scope, 'env>) -> Result<R>,
) -> Result<R> {
f(&mut Scope::new(self.lock_arc()))
}
/// Attempts to coerce a Lua value into a String in a manner consistent with Lua's internal
/// behavior.
+2 -2
View File
@@ -35,7 +35,7 @@ pub(crate) struct ExtraData {
pub(super) weak: MaybeUninit<WeakLua>,
pub(super) owned: bool,
pub(super) registered_userdata: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_t: FxHashMap<TypeId, c_int>,
pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option<TypeId>>,
pub(super) last_checked_userdata_mt: (*const c_void, Option<TypeId>),
@@ -144,7 +144,7 @@ impl ExtraData {
lua: MaybeUninit::uninit(),
weak: MaybeUninit::uninit(),
owned,
registered_userdata: FxHashMap::default(),
registered_userdata_t: FxHashMap::default(),
registered_userdata_mt: FxHashMap::default(),
last_checked_userdata_mt: (ptr::null(), None),
registry_unref_list: Arc::new(Mutex::new(Some(Vec::new()))),
+50 -56
View File
@@ -20,7 +20,7 @@ use crate::types::{
AppDataRef, AppDataRefMut, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData,
MaybeSend, ReentrantMutex, RegistryKey, SubtypeId, ValueRef, XRc,
};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataVariant};
use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataRegistry, UserDataStorage};
use crate::util::{
assert_stack, check_stack, get_destructed_userdata_metatable, get_internal_userdata, get_main_state,
get_userdata, init_error_registry, init_internal_metatable, init_userdata_metatable, pop_error,
@@ -711,45 +711,45 @@ impl RawLua {
}
}
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_userdata<T>(&self, data: UserDataStorage<T>) -> Result<AnyUserData>
where
T: UserData + '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) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}
// Create a new metatable from `UserData` definition
let mut registry = const { UserDataRegistry::new() };
let mut registry = UserDataRegistry::new(type_id);
T::register(&mut registry);
self.register_userdata_metatable(registry)
self.create_userdata_metatable(registry)
})
}
pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataVariant<T>) -> Result<AnyUserData>
pub(crate) unsafe fn make_any_userdata<T>(&self, data: UserDataStorage<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) {
if let Some(&table_id) = (*self.extra.get()).registered_userdata_t.get(&type_id) {
return Ok(table_id as Integer);
}
// Create an empty metatable
let registry = const { UserDataRegistry::new() };
self.register_userdata_metatable::<T>(registry)
let registry = UserDataRegistry::<T>::new(type_id);
self.create_userdata_metatable(registry)
})
}
unsafe fn make_userdata_with_metatable<T>(
&self,
data: UserDataVariant<T>,
data: UserDataStorage<T>,
get_metatable_id: impl FnOnce() -> Result<Integer>,
) -> Result<AnyUserData> {
let state = self.state();
@@ -760,10 +760,7 @@ impl RawLua {
ffi::lua_pushnil(state);
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, get_metatable_id()?);
let protect = !self.unlikely_memory_error();
#[cfg(not(feature = "lua54"))]
crate::util::push_userdata(state, data, protect)?;
#[cfg(feature = "lua54")]
crate::util::push_userdata_uv(state, data, crate::userdata::USER_VALUE_MAXSLOT as c_int, protect)?;
ffi::lua_replace(state, -3);
ffi::lua_setmetatable(state, -2);
@@ -782,12 +779,31 @@ impl RawLua {
Ok(AnyUserData(self.pop_ref(), SubtypeId::None))
}
pub(crate) unsafe fn register_userdata_metatable<T: 'static>(
pub(crate) unsafe fn create_userdata_metatable<T>(
&self,
mut registry: UserDataRegistry<T>,
registry: UserDataRegistry<T>,
) -> Result<Integer> {
let state = self.state();
let _sg = StackGuard::new(state);
let type_id = registry.type_id();
self.push_userdata_metatable(registry)?;
let mt_ptr = ffi::lua_topointer(state, -1);
let id = protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
if let Some(type_id) = type_id {
(*self.extra.get()).registered_userdata_t.insert(type_id, id);
}
self.register_userdata_metatable(mt_ptr, type_id);
Ok(id as Integer)
}
pub(crate) unsafe fn push_userdata_metatable<T>(&self, mut registry: UserDataRegistry<T>) -> Result<()> {
let state = self.state();
let _sg = StackGuard::with_top(state, ffi::lua_gettop(state) + 1);
check_stack(state, 13)?;
// Prepare metatable, add meta methods first and then meta fields
@@ -922,7 +938,7 @@ impl RawLua {
let extra_init = None;
#[cfg(not(feature = "luau"))]
let extra_init: Option<fn(*mut ffi::lua_State) -> Result<()>> = Some(|state| {
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataVariant<T>>);
ffi::lua_pushcfunction(state, crate::util::userdata_destructor::<UserDataStorage<T>>);
rawset_field(state, -2, "__gc")
});
@@ -938,44 +954,21 @@ impl RawLua {
// Pop extra tables to get metatable on top of the stack
ffi::lua_pop(state, extra_tables_count);
let mt_ptr = ffi::lua_topointer(state, -1);
let id = protect_lua!(state, 1, 0, |state| {
ffi::luaL_ref(state, ffi::LUA_REGISTRYINDEX)
})?;
let type_id = TypeId::of::<T>();
(*self.extra.get()).registered_userdata.insert(type_id, id);
(*self.extra.get())
.registered_userdata_mt
.insert(mt_ptr, Some(type_id));
Ok(id as Integer)
Ok(())
}
// #[inline]
// pub(crate) unsafe fn register_raw_userdata_metatable(
// &self,
// ptr: *const c_void,
// type_id: Option<TypeId>,
// ) {
// (*self.extra.get())
// .registered_userdata_mt
// .insert(ptr, type_id);
// }
#[inline(always)]
pub(crate) unsafe fn register_userdata_metatable(&self, mt_ptr: *const c_void, type_id: Option<TypeId>) {
(*self.extra.get()).registered_userdata_mt.insert(mt_ptr, type_id);
}
// #[inline]
// pub(crate) unsafe fn deregister_raw_userdata_metatable(&self, ptr: *const c_void) {
// (*self.extra.get()).registered_userdata_mt.remove(&ptr);
// if (*self.extra.get()).last_checked_userdata_mt.0 == ptr {
// (*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
// }
// }
// #[inline(always)]
// pub(crate) unsafe fn get_userdata_ref<T: 'static>(&self, idx: c_int) -> Result<UserDataRef<T>> {
// let guard = self.lua().lock_arc();
// (*get_userdata::<UserDataVariant<T>>(self.state(), idx)).try_make_ref(guard)
// }
#[inline(always)]
pub(crate) unsafe fn deregister_userdata_metatable(&self, mt_ptr: *const c_void) {
(*self.extra.get()).registered_userdata_mt.remove(&mt_ptr);
if (*self.extra.get()).last_checked_userdata_mt.0 == mt_ptr {
(*self.extra.get()).last_checked_userdata_mt = (ptr::null(), None);
}
}
// Returns `TypeId` for the userdata ref, checking that it's registered and not destructed.
//
@@ -1028,8 +1021,6 @@ impl RawLua {
// Creates a Function out of a Callback containing a 'static Fn.
pub(crate) fn create_callback(&self, func: Callback) -> Result<Function> {
// This is non-scoped version of the callback (upvalue is always valid)
// TODO: add a scoped version
unsafe extern "C-unwind" fn call_callback(state: *mut ffi::lua_State) -> c_int {
let upvalue = get_userdata::<CallbackUpvalue>(state, ffi::lua_upvalueindex(1));
callback_error_ext(state, (*upvalue).extra.get(), |extra, nargs| {
@@ -1037,8 +1028,10 @@ impl RawLua {
// The lock must be already held as the callback is executed
let rawlua = (*extra).raw_lua();
let _guard = StateGuard::new(rawlua, state);
let func = &*(*upvalue).data;
func(rawlua, nargs)
match (*upvalue).data {
Some(ref func) => func(rawlua, nargs),
None => Err(Error::CallbackDestructed),
}
})
}
@@ -1047,6 +1040,7 @@ impl RawLua {
let _sg = StackGuard::new(state);
check_stack(state, 4)?;
let func = Some(func);
let extra = XRc::clone(&self.extra);
let protect = !self.unlikely_memory_error();
push_internal_userdata(state, CallbackUpvalue { data: func, extra }, protect)?;
+3 -1
View File
@@ -52,12 +52,14 @@ pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + Send +
#[cfg(not(feature = "send"))]
pub(crate) type Callback = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 'static>;
pub(crate) type ScopedCallback<'s> = Box<dyn Fn(&RawLua, c_int) -> Result<c_int> + 's>;
pub(crate) struct Upvalue<T> {
pub(crate) data: T,
pub(crate) extra: XRc<UnsafeCell<ExtraData>>,
}
pub(crate) type CallbackUpvalue = Upvalue<Callback>;
pub(crate) type CallbackUpvalue = Upvalue<Option<Callback>>;
#[cfg(all(feature = "async", feature = "send"))]
pub(crate) type AsyncCallback =
+38 -62
View File
@@ -2,7 +2,7 @@ use std::any::TypeId;
use std::ffi::CStr;
use std::fmt;
use std::hash::Hash;
use std::os::raw::{c_char, c_int, c_void};
use std::os::raw::{c_char, c_void};
use std::string::String as StdString;
#[cfg(feature = "async")]
@@ -16,7 +16,7 @@ use {
use crate::error::{Error, Result};
use crate::function::Function;
use crate::state::{Lua, LuaGuard};
use crate::state::Lua;
use crate::string::String;
use crate::table::{Table, TablePairs};
use crate::types::{MaybeSend, SubtypeId, ValueRef};
@@ -24,14 +24,11 @@ use crate::util::{check_stack, get_userdata, take_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
// Re-export for convenience
pub(crate) use cell::UserDataVariant;
pub(crate) use cell::UserDataStorage;
pub use cell::{UserDataRef, UserDataRefMut};
pub(crate) use registry::UserDataProxy;
pub use registry::UserDataRegistry;
#[cfg(feature = "lua54")]
pub(crate) const USER_VALUE_MAXSLOT: usize = 8;
/// Kinds of metamethods that can be overridden.
///
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
@@ -650,8 +647,9 @@ pub struct AnyUserData(pub(crate) ValueRef, pub(crate) SubtypeId);
impl AnyUserData {
/// Checks whether the type of this userdata is `T`.
#[inline]
pub fn is<T: 'static>(&self) -> bool {
self.inspect::<T, _, _>(|_, _| Ok(())).is_ok()
self.inspect::<T, _, _>(|_| Ok(())).is_ok()
}
/// Borrow this userdata immutably if it is of type `T`.
@@ -659,10 +657,18 @@ impl AnyUserData {
/// # Errors
///
/// Returns a `UserDataBorrowError` if the userdata is already mutably borrowed. Returns a
/// `UserDataTypeMismatch` if the userdata is not of type `T`.
/// `UserDataTypeMismatch` if the userdata is not of type `T` or if it's scoped.
#[inline]
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
self.inspect(|variant, _| variant.try_borrow_owned())
self.inspect(|ud| ud.try_borrow_owned())
}
/// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
/// to the closure.
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped(|ud| f(ud)))
}
/// Borrow this userdata mutably if it is of type `T`.
@@ -670,10 +676,18 @@ impl AnyUserData {
/// # Errors
///
/// Returns a `UserDataBorrowMutError` if the userdata cannot be mutably borrowed.
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T`.
/// Returns a `UserDataTypeMismatch` if the userdata is not of type `T` or if it's scoped.
#[inline]
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
self.inspect(|variant, _| variant.try_borrow_owned_mut())
self.inspect(|ud| ud.try_borrow_owned_mut())
}
/// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
/// to the closure.
///
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
self.inspect(|ud| ud.try_borrow_scoped_mut(|ud| f(ud)))
}
/// Takes the value out of this userdata.
@@ -692,8 +706,8 @@ impl AnyUserData {
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
// Try to borrow userdata exclusively
let _ = (*get_userdata::<UserDataVariant<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataVariant<T>>(state).into_inner()
let _ = (*get_userdata::<UserDataStorage<T>>(state, -1)).try_borrow_mut()?;
take_userdata::<UserDataStorage<T>>(state).into_inner()
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -754,29 +768,16 @@ impl AnyUserData {
lua.push_userdata_ref(&self.0)?;
lua.push(v)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_setiuservalue(state, -2, n as c_int);
return Ok(());
}
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushvalue(state, -2);
#[cfg(feature = "lua54")]
ffi::lua_rawseti(state, -2, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawseti(state, -2, n as ffi::lua_Integer);
})?;
@@ -806,21 +807,12 @@ impl AnyUserData {
lua.push_userdata_ref(&self.0)?;
#[cfg(feature = "lua54")]
if n < USER_VALUE_MAXSLOT {
ffi::lua_getiuservalue(state, -1, n as c_int);
return V::from_lua(lua.pop_value(), lua.lua());
}
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
#[cfg(feature = "lua54")]
ffi::lua_rawgeti(state, -1, (n - USER_VALUE_MAXSLOT + 1) as ffi::lua_Integer);
#[cfg(not(feature = "lua54"))]
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
})?;
@@ -851,15 +843,11 @@ impl AnyUserData {
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 2, 0, |state| {
if getuservalue_table(state, -2) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
// Create a new table to use as uservalue
ffi::lua_pop(state, 1);
ffi::lua_newtable(state);
ffi::lua_pushvalue(state, -1);
#[cfg(feature = "lua54")]
ffi::lua_setiuservalue(state, -4, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
ffi::lua_setuservalue(state, -4);
}
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
@@ -885,7 +873,7 @@ impl AnyUserData {
// Multiple (extra) user values are emulated by storing them in a table
protect_lua!(state, 1, 1, |state| {
if getuservalue_table(state, -1) != ffi::LUA_TTABLE {
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
ffi::lua_pushnil(state);
return;
}
@@ -998,29 +986,24 @@ impl AnyUserData {
let is_serializable = || unsafe {
// Userdata must be registered and not destructed
let _ = lua.get_userdata_ref_type_id(&self.0)?;
let ud = &*get_userdata::<UserDataVariant<()>>(lua.ref_thread(), self.0.index);
match ud {
UserDataVariant::Serializable(..) => Result::Ok(true),
_ => Result::Ok(false),
}
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
Ok::<_, Error>((*ud).is_serializable())
};
is_serializable().unwrap_or(false)
}
pub(crate) fn inspect<'a, T, F, R>(&'a self, func: F) -> Result<R>
pub(crate) fn inspect<T, F, R>(&self, func: F) -> Result<R>
where
T: 'static,
F: FnOnce(&'a UserDataVariant<T>, LuaGuard) -> Result<R>,
F: FnOnce(&UserDataStorage<T>) -> Result<R>,
{
let lua = self.0.lua.lock();
unsafe {
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
let ref_thread = lua.ref_thread();
let ud = get_userdata::<UserDataVariant<T>>(ref_thread, self.0.index);
func(&*ud, lua)
let ud = get_userdata::<UserDataStorage<T>>(lua.ref_thread(), self.0.index);
func(&*ud)
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -1041,13 +1024,6 @@ impl AsRef<AnyUserData> for AnyUserData {
}
}
unsafe fn getuservalue_table(state: *mut ffi::lua_State, idx: c_int) -> c_int {
#[cfg(feature = "lua54")]
return ffi::lua_getiuservalue(state, idx, USER_VALUE_MAXSLOT as c_int);
#[cfg(not(feature = "lua54"))]
return ffi::lua_getuservalue(state, idx);
}
/// Handle to a `UserData` metatable.
#[derive(Clone, Debug)]
pub struct UserDataMetatable(pub(crate) Table);
@@ -1146,7 +1122,7 @@ impl Serialize for AnyUserData {
let _ = lua
.get_userdata_ref_type_id(&self.0)
.map_err(ser::Error::custom)?;
let ud = &*get_userdata::<UserDataVariant<()>>(lua.ref_thread(), self.0.index);
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
ud.serialize(serializer)
}
}
+135 -26
View File
@@ -1,5 +1,5 @@
use std::any::{type_name, TypeId};
use std::cell::UnsafeCell;
use std::cell::{RefCell, UnsafeCell};
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::os::raw::c_int;
@@ -22,6 +22,11 @@ type DynSerialize = dyn erased_serde::Serialize;
#[cfg(all(feature = "serialize", feature = "send"))]
type DynSerialize = dyn erased_serde::Serialize + Send;
pub(crate) enum UserDataStorage<T> {
Owned(UserDataVariant<T>),
Scoped(ScopedUserDataVariant<T>),
}
// A enum for storing userdata values.
// It's stored inside a Lua VM and protected by the outer `ReentrantMutex`.
pub(crate) enum UserDataVariant<T> {
@@ -42,39 +47,34 @@ impl<T> Clone for UserDataVariant<T> {
}
impl<T> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new(data: T) -> Self {
Self::Default(XRc::new(UserDataCell::new(data)))
}
// Immutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
fn try_borrow(&self) -> Result<UserDataBorrowRef<T>> {
UserDataBorrowRef::try_from(self)
}
// Immutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
UserDataRef::try_from(self.clone())
}
// Mutably borrows the wrapped value in-place.
#[inline(always)]
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
UserDataBorrowMut::try_from(self)
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
UserDataRefMut::try_from(self.clone())
}
// Returns the wrapped value.
//
// This method checks that we have exclusive access to the value.
pub(crate) fn into_inner(self) -> Result<T> {
fn into_inner(self) -> Result<T> {
if !self.raw_lock().try_lock_exclusive() {
return Err(Error::UserDataBorrowMutError);
}
@@ -108,20 +108,10 @@ impl<T> UserDataVariant<T> {
}
#[cfg(feature = "serialize")]
impl<T: Serialize + MaybeSend + 'static> UserDataVariant<T> {
#[inline(always)]
pub(crate) fn new_ser(data: T) -> Self {
let data = Box::new(data) as Box<DynSerialize>;
Self::Serializable(XRc::new(UserDataCell::new(data)))
}
}
#[cfg(feature = "serialize")]
impl Serialize for UserDataVariant<()> {
impl Serialize for UserDataStorage<()> {
fn serialize<S: Serializer>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> {
match self {
Self::Default(_) => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
Self::Serializable(inner) => unsafe {
Self::Owned(UserDataVariant::Serializable(inner)) => unsafe {
// We need to borrow the inner value exclusively to serialize it.
#[cfg(feature = "send")]
let _guard = self.try_borrow_mut().map_err(serde::ser::Error::custom)?;
@@ -130,6 +120,7 @@ impl Serialize for UserDataVariant<()> {
let _guard = self.try_borrow().map_err(serde::ser::Error::custom)?;
(*inner.value.get()).serialize(serializer)
},
_ => Err(serde::ser::Error::custom("cannot serialize <userdata>")),
}
}
}
@@ -145,7 +136,7 @@ unsafe impl<T: Send> Sync for UserDataCell<T> {}
impl<T> UserDataCell<T> {
#[inline(always)]
pub fn new(value: T) -> Self {
fn new(value: T) -> Self {
UserDataCell {
raw_lock: RawLock::INIT,
value: UnsafeCell::new(value),
@@ -207,7 +198,7 @@ impl<T: 'static> FromLua for UserDataRef<T> {
let type_id = lua.get_userdata_type_id(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned()
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned()
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -275,7 +266,7 @@ impl<T: 'static> FromLua for UserDataRefMut<T> {
let type_id = lua.get_userdata_type_id(idx)?;
match type_id {
Some(type_id) if type_id == TypeId::of::<T>() => {
(*get_userdata::<UserDataVariant<T>>(lua.state(), idx)).try_borrow_owned_mut()
(*get_userdata::<UserDataStorage<T>>(lua.state(), idx)).try_borrow_owned_mut()
}
_ => Err(Error::UserDataTypeMismatch),
}
@@ -363,6 +354,124 @@ fn try_value_to_userdata<T>(value: Value) -> Result<AnyUserData> {
}
}
pub(crate) enum ScopedUserDataVariant<T> {
Ref(*const T),
RefMut(RefCell<*mut T>),
Boxed(RefCell<*mut T>),
}
impl<T> Drop for ScopedUserDataVariant<T> {
#[inline]
fn drop(&mut self) {
if let Self::Boxed(value) = self {
if let Ok(value) = value.try_borrow_mut() {
unsafe { drop(Box::from_raw(*value)) };
}
}
}
}
impl<T: 'static> UserDataStorage<T> {
#[inline(always)]
pub(crate) fn new(data: T) -> Self {
Self::Owned(UserDataVariant::Default(XRc::new(UserDataCell::new(data))))
}
#[inline(always)]
pub(crate) fn new_ref(data: &T) -> Self {
Self::Scoped(ScopedUserDataVariant::Ref(data))
}
#[inline(always)]
pub(crate) fn new_ref_mut(data: &mut T) -> Self {
Self::Scoped(ScopedUserDataVariant::RefMut(RefCell::new(data)))
}
#[cfg(feature = "serialize")]
#[inline(always)]
pub(crate) fn new_ser(data: T) -> Self
where
T: Serialize + MaybeSend,
{
let data = Box::new(data) as Box<DynSerialize>;
Self::Owned(UserDataVariant::Serializable(XRc::new(UserDataCell::new(data))))
}
#[cfg(feature = "serialize")]
#[inline(always)]
pub(crate) fn is_serializable(&self) -> bool {
matches!(self, Self::Owned(UserDataVariant::Serializable(_)))
}
// Immutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned(&self) -> Result<UserDataRef<T>> {
match self {
Self::Owned(data) => data.try_borrow_owned(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[inline(always)]
pub(crate) fn try_borrow_mut(&self) -> Result<UserDataBorrowMut<T>> {
match self {
Self::Owned(data) => data.try_borrow_mut(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
// Mutably borrows the wrapped value and returns an owned reference.
#[inline(always)]
pub(crate) fn try_borrow_owned_mut(&self) -> Result<UserDataRefMut<T>> {
match self {
Self::Owned(data) => data.try_borrow_owned_mut(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
#[inline(always)]
pub(crate) fn into_inner(self) -> Result<T> {
match self {
Self::Owned(data) => data.into_inner(),
Self::Scoped(_) => Err(Error::UserDataTypeMismatch),
}
}
}
impl<T> UserDataStorage<T> {
#[inline(always)]
pub(crate) fn new_scoped(data: T) -> Self {
let data = Box::into_raw(Box::new(data));
Self::Scoped(ScopedUserDataVariant::Boxed(RefCell::new(data)))
}
#[inline]
pub(crate) fn try_borrow_scoped<R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&*data.try_borrow()?)),
Self::Scoped(ScopedUserDataVariant::Ref(value)) => Ok(f(unsafe { &**value })),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let t = value.try_borrow().map_err(|_| Error::UserDataBorrowError)?;
Ok(f(unsafe { &**t }))
}
}
}
#[inline]
pub(crate) fn try_borrow_scoped_mut<R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
match self {
Self::Owned(data) => Ok(f(&mut *data.try_borrow_mut()?)),
Self::Scoped(ScopedUserDataVariant::Ref(_)) => Err(Error::UserDataBorrowMutError),
Self::Scoped(ScopedUserDataVariant::RefMut(value) | ScopedUserDataVariant::Boxed(value)) => {
let mut t = value
.try_borrow_mut()
.map_err(|_| Error::UserDataBorrowMutError)?;
Ok(f(unsafe { &mut **t }))
}
}
}
}
#[cfg(test)]
mod assertions {
use super::*;
+121 -64
View File
@@ -3,7 +3,7 @@
use std::any::TypeId;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::os::raw::c_int;
use std::os::raw::c_void;
use std::string::String as StdString;
use crate::error::{Error, Result};
@@ -11,12 +11,11 @@ use crate::state::{Lua, RawLua};
use crate::types::{Callback, MaybeSend};
use crate::userdata::{
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataRef, UserDataRefMut,
UserDataStorage,
};
use crate::util::{get_userdata, short_type_name};
use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Value};
use super::cell::{UserDataBorrowMut, UserDataBorrowRef, UserDataVariant};
#[cfg(feature = "async")]
use {
crate::types::AsyncCallback,
@@ -25,8 +24,14 @@ use {
type StaticFieldCallback = Box<dyn FnOnce(&RawLua) -> Result<()> + 'static>;
#[derive(Clone, Copy)]
pub(crate) enum UserDataTypeId {
Shared(TypeId),
Unique(usize),
}
/// Handle to registry for userdata methods and metamethods.
pub struct UserDataRegistry<T: 'static> {
pub struct UserDataRegistry<T> {
// Fields
pub(crate) fields: Vec<(String, StaticFieldCallback)>,
pub(crate) field_getters: Vec<(String, Callback)>,
@@ -41,11 +46,13 @@ pub struct UserDataRegistry<T: 'static> {
#[cfg(feature = "async")]
pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>,
pub(crate) type_id: UserDataTypeId,
_type: PhantomData<T>,
}
impl<T: 'static> UserDataRegistry<T> {
pub(crate) const fn new() -> Self {
impl<T> UserDataRegistry<T> {
#[inline]
pub(crate) fn new(type_id: TypeId) -> Self {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
@@ -57,11 +64,38 @@ impl<T: 'static> UserDataRegistry<T> {
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
type_id: UserDataTypeId::Shared(type_id),
_type: PhantomData,
}
}
fn box_method<M, A, R>(name: &str, method: M) -> Callback
#[inline]
pub(crate) fn new_unique(ud_ptr: *const c_void) -> Self {
UserDataRegistry {
fields: Vec::new(),
field_getters: Vec::new(),
field_setters: Vec::new(),
meta_fields: Vec::new(),
methods: Vec::new(),
#[cfg(feature = "async")]
async_methods: Vec::new(),
meta_methods: Vec::new(),
#[cfg(feature = "async")]
async_meta_methods: Vec::new(),
type_id: UserDataTypeId::Unique(ud_ptr as usize),
_type: PhantomData,
}
}
#[inline]
pub(crate) fn type_id(&self) -> Option<TypeId> {
match self.type_id {
UserDataTypeId::Shared(type_id) => Some(type_id),
UserDataTypeId::Unique(_) => None,
}
}
fn box_method<M, A, R>(&self, name: &str, method: M) -> Callback
where
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -74,6 +108,7 @@ impl<T: 'static> UserDataRegistry<T> {
};
}
let target_type_id = self.type_id;
Box::new(move |rawlua, nargs| unsafe {
if nargs == 0 {
let err = Error::from_lua_conversion("missing argument", "userdata", None);
@@ -85,17 +120,34 @@ impl<T: 'static> UserDataRegistry<T> {
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
Some(id) if id == TypeId::of::<T>() => {
let ud = try_self_arg!(borrow_userdata_ref::<T>(state, self_index));
method(rawlua.lua(), &ud, args?)?.push_into_stack_multi(rawlua)
match target_type_id {
// This branch is for `'static` userdata that share type metatable
UserDataTypeId::Shared(target_type_id) => {
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
Some(self_type_id) if self_type_id == target_type_id => {
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
UserDataTypeId::Unique(target_ptr) => {
match get_userdata::<UserDataStorage<T>>(state, self_index) {
ud if ud as usize == target_ptr => {
try_self_arg!((*ud).try_borrow_scoped(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
fn box_method_mut<M, A, R>(name: &str, method: M) -> Callback
fn box_method_mut<M, A, R>(&self, name: &str, method: M) -> Callback
where
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -109,6 +161,7 @@ impl<T: 'static> UserDataRegistry<T> {
}
let method = RefCell::new(method);
let target_type_id = self.type_id;
Box::new(move |rawlua, nargs| unsafe {
let mut method = method.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?;
if nargs == 0 {
@@ -121,19 +174,37 @@ impl<T: 'static> UserDataRegistry<T> {
// Self was at position 1, so we pass 2 here
let args = A::from_stack_args(nargs - 1, 2, Some(&name), rawlua);
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
Some(id) if id == TypeId::of::<T>() => {
let mut ud = try_self_arg!(borrow_userdata_mut::<T>(state, self_index));
method(rawlua.lua(), &mut ud, args?)?.push_into_stack_multi(rawlua)
match target_type_id {
// This branch is for `'static` userdata that share type metatable
UserDataTypeId::Shared(target_type_id) => {
match try_self_arg!(rawlua.get_userdata_type_id(self_index)) {
Some(self_type_id) if self_type_id == target_type_id => {
let ud = get_userdata::<UserDataStorage<T>>(state, self_index);
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
UserDataTypeId::Unique(target_ptr) => {
match get_userdata::<UserDataStorage<T>>(state, self_index) {
ud if ud as usize == target_ptr => {
try_self_arg!((*ud).try_borrow_scoped_mut(|ud| {
method(rawlua.lua(), ud, args?)?.push_into_stack_multi(rawlua)
}))
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
}
_ => Err(Error::bad_self_argument(&name, Error::UserDataTypeMismatch)),
}
})
}
#[cfg(feature = "async")]
fn box_async_method<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
fn box_async_method<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
where
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
@@ -171,8 +242,9 @@ impl<T: 'static> UserDataRegistry<T> {
}
#[cfg(feature = "async")]
fn box_async_method_mut<M, A, MR, R>(name: &str, method: M) -> AsyncCallback
fn box_async_method_mut<M, A, MR, R>(&self, name: &str, method: M) -> AsyncCallback
where
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
@@ -209,7 +281,7 @@ impl<T: 'static> UserDataRegistry<T> {
})
}
fn box_function<F, A, R>(name: &str, function: F) -> Callback
fn box_function<F, A, R>(&self, name: &str, function: F) -> Callback
where
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -222,7 +294,7 @@ impl<T: 'static> UserDataRegistry<T> {
})
}
fn box_function_mut<F, A, R>(name: &str, function: F) -> Callback
fn box_function_mut<F, A, R>(&self, name: &str, function: F) -> Callback
where
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
A: FromLuaMulti,
@@ -240,7 +312,7 @@ impl<T: 'static> UserDataRegistry<T> {
}
#[cfg(feature = "async")]
fn box_async_function<F, A, FR, R>(name: &str, function: F) -> AsyncCallback
fn box_async_function<F, A, FR, R>(&self, name: &str, function: F) -> AsyncCallback
where
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
A: FromLuaMulti,
@@ -282,7 +354,7 @@ fn get_function_name<T>(name: &str) -> StdString {
format!("{}.{name}", short_type_name::<T>())
}
impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
impl<T> UserDataFields<T> for UserDataRegistry<T> {
fn add_field<V>(&mut self, name: impl ToString, value: V)
where
V: IntoLua + 'static,
@@ -300,7 +372,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_method(&name, move |lua, data, ()| method(lua, data));
let callback = self.box_method(&name, move |lua, data, ()| method(lua, data));
self.field_getters.push((name, callback));
}
@@ -310,7 +382,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.field_setters.push((name, callback));
}
@@ -320,7 +392,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
R: IntoLua,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.field_getters.push((name, callback));
}
@@ -330,7 +402,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
A: FromLua,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
let callback = self.box_function_mut(&name, move |lua, (data, val)| function(lua, data, val));
self.field_setters.push((name, callback));
}
@@ -363,7 +435,7 @@ impl<T: 'static> UserDataFields<T> for UserDataRegistry<T> {
}
}
impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
impl<T> UserDataMethods<T> for UserDataRegistry<T> {
fn add_method<M, A, R>(&mut self, name: impl ToString, method: M)
where
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
@@ -371,7 +443,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
let callback = self.box_method(&name, method);
self.methods.push((name, callback));
}
@@ -382,33 +454,35 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
let callback = self.box_async_method(&name, method);
self.async_methods.push((name, callback));
}
#[cfg(feature = "async")]
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
let callback = self.box_async_method_mut(&name, method);
self.async_methods.push((name, callback));
}
@@ -419,7 +493,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.methods.push((name, callback));
}
@@ -430,7 +504,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
let callback = self.box_function_mut(&name, function);
self.methods.push((name, callback));
}
@@ -443,7 +517,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
let callback = self.box_async_function(&name, function);
self.async_methods.push((name, callback));
}
@@ -454,7 +528,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method(&name, method);
let callback = self.box_method(&name, method);
self.meta_methods.push((name, callback));
}
@@ -465,33 +539,35 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_method_mut(&name, method);
let callback = self.box_method_mut(&name, method);
self.meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method(&name, method);
let callback = self.box_async_method(&name, method);
self.async_meta_methods.push((name, callback));
}
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl ToString, method: M)
where
T: 'static,
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
A: FromLuaMulti,
MR: Future<Output = Result<R>> + MaybeSend + 'static,
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_method_mut(&name, method);
let callback = self.box_async_method_mut(&name, method);
self.async_meta_methods.push((name, callback));
}
@@ -502,7 +578,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function(&name, function);
let callback = self.box_function(&name, function);
self.meta_methods.push((name, callback));
}
@@ -513,7 +589,7 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_function_mut(&name, function);
let callback = self.box_function_mut(&name, function);
self.meta_methods.push((name, callback));
}
@@ -526,36 +602,17 @@ impl<T: 'static> UserDataMethods<T> for UserDataRegistry<T> {
R: IntoLuaMulti,
{
let name = name.to_string();
let callback = Self::box_async_function(&name, function);
let callback = self.box_async_function(&name, function);
self.async_meta_methods.push((name, callback));
}
}
// Borrow the userdata in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_ref<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowRef<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow()
}
// Borrow the userdata mutably in-place from the Lua stack
#[inline(always)]
unsafe fn borrow_userdata_mut<'a, T>(
state: *mut ffi::lua_State,
index: c_int,
) -> Result<UserDataBorrowMut<'a, T>> {
let ud = get_userdata::<UserDataVariant<T>>(state, index);
(*ud).try_borrow_mut()
}
macro_rules! lua_userdata_impl {
($type:ty) => {
impl<T: UserData + 'static> UserData for $type {
fn register(registry: &mut UserDataRegistry<Self>) {
let mut orig_registry = UserDataRegistry::new();
let type_id = TypeId::of::<T>();
let mut orig_registry = UserDataRegistry::new(type_id);
T::register(&mut orig_registry);
// Copy all fields, methods, etc. from the original registry
+1 -1
View File
@@ -365,7 +365,7 @@ pub(crate) unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<(
// Create destructed userdata metatable
unsafe extern "C-unwind" fn destructed_error(state: *mut ffi::lua_State) -> c_int {
callback_error(state, |_| Err(Error::CallbackDestructed))
callback_error(state, |_| Err(Error::UserDataDestructed))
}
push_table(state, 0, 26, true)?;
+2 -3
View File
@@ -17,10 +17,9 @@ pub(crate) use userdata::{
DESTRUCTED_USERDATA_METATABLE,
};
#[cfg(not(feature = "lua54"))]
#[cfg(not(feature = "luau"))]
pub(crate) use userdata::push_uninit_userdata;
pub(crate) use userdata::push_userdata;
#[cfg(feature = "lua54")]
pub(crate) use userdata::push_userdata_uv;
#[cfg(not(feature = "luau"))]
pub(crate) use userdata::userdata_destructor;
+17 -29
View File
@@ -83,46 +83,34 @@ pub(crate) unsafe fn get_internal_userdata<T: TypeKey>(
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
#[cfg(not(feature = "luau"))]
let ud = if protect {
#[cfg(not(feature = "luau"))]
pub(crate) unsafe fn push_uninit_userdata<T>(state: *mut ffi::lua_State, protect: bool) -> Result<*mut T> {
if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
})?
})
} else {
ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T
};
Ok(ffi::lua_newuserdata(state, std::mem::size_of::<T>()) as *mut T)
}
}
// Internally uses 3 stack spaces, does not call checkstack.
#[inline]
pub(crate) unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<*mut T> {
#[cfg(not(feature = "luau"))]
let ud_ptr = push_uninit_userdata(state, protect)?;
#[cfg(feature = "luau")]
let ud = if protect {
let ud_ptr = if protect {
protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::<T>(state) })?
} else {
ffi::lua_newuserdata_t::<T>(state)
};
ptr::write(ud, t);
Ok(())
}
// Internally uses 3 stack spaces, does not call checkstack.
#[cfg(feature = "lua54")]
#[inline]
pub(crate) unsafe fn push_userdata_uv<T>(
state: *mut ffi::lua_State,
t: T,
nuvalue: c_int,
protect: bool,
) -> Result<()> {
let ud = if protect {
protect_lua!(state, 0, 1, |state| {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
})?
} else {
ffi::lua_newuserdatauv(state, std::mem::size_of::<T>(), nuvalue) as *mut T
};
ptr::write(ud, t);
Ok(())
ptr::write(ud_ptr, t);
Ok(ud_ptr)
}
#[inline]
#[track_caller]
pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T;
mlua_debug_assert!(!ud.is_null(), "userdata pointer is null");
-3
View File
@@ -7,8 +7,6 @@ fn test_compilation() {
t.compile_fail("tests/compile/lua_norefunwindsafe.rs");
t.compile_fail("tests/compile/ref_nounwindsafe.rs");
t.compile_fail("tests/compile/scope_callback_capture.rs");
t.compile_fail("tests/compile/scope_callback_inner.rs");
t.compile_fail("tests/compile/scope_callback_outer.rs");
t.compile_fail("tests/compile/scope_invariance.rs");
t.compile_fail("tests/compile/scope_mutable_aliasing.rs");
t.compile_fail("tests/compile/scope_userdata_borrow.rs");
@@ -17,7 +15,6 @@ fn test_compilation() {
{
t.compile_fail("tests/compile/async_any_userdata_method.rs");
t.compile_fail("tests/compile/async_nonstatic_userdata.rs");
t.compile_fail("tests/compile/async_userdata_method.rs");
}
#[cfg(feature = "send")]
+5 -4
View File
@@ -1,4 +1,4 @@
use mlua::{UserDataMethods, Lua};
use mlua::{Lua, UserDataMethods};
fn main() {
let lua = Lua::new();
@@ -6,9 +6,10 @@ fn main() {
lua.register_userdata_type::<String>(|reg| {
let s = String::new();
let mut s = &s;
reg.add_async_method("t", |_, this: &String, ()| async {
s = this;
reg.add_async_method("t", |_, this, ()| async {
s = &*this;
Ok(())
});
}).unwrap();
})
.unwrap();
}
+42 -42
View File
@@ -1,20 +1,42 @@
error[E0596]: cannot borrow `s` as mutable, as it is a captured variable in a `Fn` closure
--> tests/compile/async_any_userdata_method.rs:9:58
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^ cannot borrow as mutable
10 | s = this;
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^ cannot borrow as mutable
10 | s = &*this;
| - mutable borrow occurs due to use of `s` in closure
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:58
error[E0373]: async block may outlive the current function, but it borrows `this`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ___________________________________----------------------_^
| | | |
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:58: 9:63}` contains a lifetime `'2`
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^ may outlive borrowed value `this`
10 | s = &*this;
| ---- `this` is borrowed here
|
note: async block is returned here
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this, ()| async {
| _________________________________________________^
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |_________^
help: to force the async block to take ownership of `this` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", |_, this, ()| async move {
| ++++
error: lifetime may not live long enough
--> tests/compile/async_any_userdata_method.rs:9:49
|
9 | reg.add_async_method("t", |_, this, ()| async {
| ___________________________________-------------_^
| | | |
| | | return type of closure `{async block@$DIR/tests/compile/async_any_userdata_method.rs:9:49: 9:54}` contains a lifetime `'2`
| | lifetime `'1` represents this closure's body
10 | | s = this;
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |_________^ returning this value requires that `'1` must outlive `'2`
@@ -28,53 +50,31 @@ error[E0597]: `s` does not live long enough
| - binding `s` declared here
8 | let mut s = &s;
| ^^ borrowed value does not live long enough
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
9 | / reg.add_async_method("t", |_, this, ()| async {
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |__________- argument requires that `s` is borrowed for `'static`
13 | }).unwrap();
13 | })
| - `s` dropped here while still borrowed
error[E0521]: borrowed data escapes outside of closure
--> tests/compile/async_any_userdata_method.rs:9:9
|
6 | lua.register_userdata_type::<String>(|reg| {
| ---
| |
| `reg` is a reference that is only valid in the closure body
| has type `&mut LuaUserDataRegistry<'1, std::string::String>`
...
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
11 | | Ok(())
12 | | });
| | ^
| | |
| |__________`reg` escapes the closure body here
| argument requires that `'1` must outlive `'static`
|
= note: requirement occurs because of a mutable reference to `LuaUserDataRegistry<'_, std::string::String>`
= note: mutable references are invariant over their type parameter
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0373]: closure may outlive the current function, but it borrows `s`, which is owned by the current function
--> tests/compile/async_any_userdata_method.rs:9:35
|
9 | reg.add_async_method("t", |_, this: &String, ()| async {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = this;
9 | reg.add_async_method("t", |_, this, ()| async {
| ^^^^^^^^^^^^^ may outlive borrowed value `s`
10 | s = &*this;
| - `s` is borrowed here
|
note: function requires argument type to outlive `'static`
--> tests/compile/async_any_userdata_method.rs:9:9
|
9 | / reg.add_async_method("t", |_, this: &String, ()| async {
10 | | s = this;
9 | / reg.add_async_method("t", |_, this, ()| async {
10 | | s = &*this;
11 | | Ok(())
12 | | });
| |__________^
help: to force the closure to take ownership of `s` (and any other referenced variables), use the `move` keyword
|
9 | reg.add_async_method("t", move |_, this: &String, ()| async {
9 | reg.add_async_method("t", move |_, this, ()| async {
| ++++
+2 -2
View File
@@ -4,8 +4,8 @@ fn main() {
#[derive(Clone)]
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
impl UserData for MyUserData<'_> {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_async_method("print", |_, data, ()| async move {
println!("{}", data.0);
Ok(())
@@ -1,11 +1,11 @@
error: lifetime may not live long enough
--> tests/compile/async_nonstatic_userdata.rs:9:13
|
7 | impl<'a> UserData for MyUserData<'a> {
| -- lifetime `'a` defined here
8 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
7 | impl UserData for MyUserData<'_> {
| -- lifetime `'1` appears in the `impl`'s self type
8 | fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
9 | / methods.add_async_method("print", |_, data, ()| async move {
10 | | println!("{}", data.0);
11 | | Ok(())
12 | | });
| |______________^ requires that `'a` must outlive `'static`
| |______________^ requires that `'1` must outlive `'static`
-14
View File
@@ -1,14 +0,0 @@
use mlua::{UserData, UserDataMethods};
struct MyUserData;
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_async_method("method", |_, this: &'static Self, ()| async {
Ok(())
});
// ^ lifetime may not live long enough
}
}
fn main() {}
@@ -1,17 +0,0 @@
warning: unused variable: `this`
--> tests/compile/async_userdata_method.rs:7:48
|
7 | methods.add_async_method("method", |_, this: &'static Self, ()| async {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_this`
|
= note: `#[warn(unused_variables)]` on by default
error: lifetime may not live long enough
--> tests/compile/async_userdata_method.rs:7:9
|
6 | fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
| ---- lifetime `'lua` defined here
7 | / methods.add_async_method("method", |_, this: &'static Self, ()| async {
8 | | Ok(())
9 | | });
| |__________^ argument requires that `'lua` must outlive `'static`
+1 -3
View File
@@ -6,7 +6,5 @@ fn main() {
let test = Test(0);
let lua = Lua::new();
let _ = lua.create_function(|_, ()| -> Result<i32> {
Ok(test.0)
});
let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
}
+14 -17
View File
@@ -1,20 +1,17 @@
error[E0373]: closure may outlive the current function, but it borrows `test.0`, which is owned by the current function
--> tests/compile/function_borrow.rs:9:33
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| ^^^^^^^^^^^^^^^^^^^^^^ may outlive borrowed value `test.0`
10 | Ok(test.0)
| ------ `test.0` is borrowed here
|
--> tests/compile/function_borrow.rs:9:33
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
| ^^^^^^^^^^^^^^^^^^^^^^ ------ `test.0` is borrowed here
| |
| may outlive borrowed value `test.0`
|
note: function requires argument type to outlive `'static`
--> tests/compile/function_borrow.rs:9:13
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> {
| _____________^
10 | | Ok(test.0)
11 | | });
| |______^
--> tests/compile/function_borrow.rs:9:13
|
9 | let _ = lua.create_function(|_, ()| -> Result<i32> { Ok(test.0) });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `test.0` (and any other referenced variables), use the `move` keyword
|
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> {
| ++++
|
9 | let _ = lua.create_function(move |_, ()| -> Result<i32> { Ok(test.0) });
| ++++
+53 -31
View File
@@ -1,32 +1,36 @@
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
note: required because it appears within the type `Cell<*mut lua_State>`
--> $RUST/core/src/cell.rs
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `mlua::state::raw::RawLua`
--> src/state/raw.rs
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct RawLua {
| ^^^^^^
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
--> src/types/sync.rs
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
--> $RUST/core/src/marker.rs
|
| pub(crate) struct ReentrantMutex<T>(T);
| ^^^^^^^^^^^^^^
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `Lua`
--> src/state.rs
|
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
| pub struct Lua {
| ^^^
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it's used within this closure
@@ -40,31 +44,49 @@ note: required by a bound in `std::panic::catch_unwind`
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/lua_norefunwindsafe.rs:7:18
|
7 | catch_unwind(|| lua.create_table().unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
note: required because it appears within the type `mlua::state::raw::RawLua`
--> src/state/raw.rs
= help: within `Lua`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe`
note: required because it appears within the type `Cell<usize>`
--> $RUST/core/src/cell.rs
|
| pub struct RawLua {
| ^^^^^^
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
--> src/types/sync.rs
| pub struct Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub(crate) struct ReentrantMutex<T>(T);
| ^^^^^^^^^^^^^^
= note: required for `Rc<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>` to implement `RefUnwindSafe`
| pub struct RawReentrantMutex<R, G> {
| ^^^^^^^^^^^^^^^^^
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
note: required because it appears within the type `PhantomData<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Arc<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Arc<
| ^^^
note: required because it appears within the type `Lua`
--> src/state.rs
|
| pub struct Lua(XRc<ReentrantMutex<RawLua>>);
| pub struct Lua {
| ^^^
= note: required for `&Lua` to implement `UnwindSafe`
note: required because it's used within this closure
+2 -4
View File
@@ -8,10 +8,8 @@ fn main() -> Result<()> {
let data = Rc::new(Cell::new(0));
lua.create_function(move |_, ()| {
Ok(data.get())
})?
.call::<i32>(())?;
lua.create_function(move |_, ()| Ok(data.get()))?
.call::<i32>(())?;
Ok(())
}
+14 -17
View File
@@ -1,28 +1,25 @@
error[E0277]: `Rc<Cell<i32>>` cannot be sent between threads safely
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
| --------------- ^-----------
| | |
| _________|_______________within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
| | |
| | required by a bound introduced by this call
12 | | Ok(data.get())
13 | | })?
| |_____^ `Rc<Cell<i32>>` cannot be sent between threads safely
11 | lua.create_function(move |_, ()| Ok(data.get()))?
| --------------- ------------^^^^^^^^^^^^^^^
| | |
| | `Rc<Cell<i32>>` cannot be sent between threads safely
| | within this `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`
| required by a bound introduced by this call
|
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`
= help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc<Cell<i32>>`, which is required by `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}: MaybeSend`
note: required because it's used within this closure
--> tests/compile/non_send.rs:11:25
|
11 | lua.create_function(move |_, ()| {
11 | lua.create_function(move |_, ()| Ok(data.get()))?
| ^^^^^^^^^^^^
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `mlua::types::MaybeSend`
= note: required for `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}` to implement `MaybeSend`
note: required by a bound in `Lua::create_function`
--> src/lua.rs
--> src/state.rs
|
| pub fn create_function<'lua, A, R, F>(&'lua self, func: F) -> Result<Function<'lua>>
| pub fn create_function<F, A, R>(&self, func: F) -> Result<Function>
| --------------- required by a bound in this associated function
...
| F: Fn(&'lua Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
| where
| F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
| ^^^^^^^^^ required by this bound in `Lua::create_function`
+69 -126
View File
@@ -1,3 +1,54 @@
error[E0277]: the type `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::raw::RawLua>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::raw::RawLua>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Weak<
| ^^^^
note: required because it appears within the type `mlua::state::WeakLua`
--> src/state.rs
|
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
| ^^^^^^^
note: required because it appears within the type `mlua::types::ValueRef`
--> src/types.rs
|
| pub(crate) struct ValueRef {
| ^^^^^^^^
note: required because it appears within the type `LuaTable`
--> src/table.rs
|
| pub struct Table(pub(crate) ValueRef);
| ^^^^^
note: required because it's used within this closure
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^
note: required by a bound in `std::panic::catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
@@ -6,138 +57,30 @@ error[E0277]: the type `UnsafeCell<usize>` may contain interior mutability and a
| |
| required by a bound introduced by this call
|
= help: within `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
= help: within `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<usize>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
note: required because it appears within the type `Cell<usize>`
--> $RUST/core/src/cell.rs
|
| pub struct Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/rc.rs
note: required because it appears within the type `lock_api::remutex::RawReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| struct RcBox<T: ?Sized> {
| ^^^^^
= note: required for `NonNull<rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/rc.rs
|
| pub struct Weak<
| ^^^^
note: required because it appears within the type `mlua::state::WeakLua`
--> src/state.rs
|
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
| ^^^^^^^
note: required because it appears within the type `mlua::types::ValueRef`
--> src/types.rs
|
| pub(crate) struct ValueRef {
| ^^^^^^^^
note: required because it appears within the type `LuaTable`
--> src/table.rs
|
| pub struct Table(pub(crate) ValueRef);
| ^^^^^
note: required because it's used within this closure
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^
note: required by a bound in `std::panic::catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
error[E0277]: the type `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<*mut lua_State>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: within `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell<*mut lua_State>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
note: required because it appears within the type `Cell<*mut lua_State>`
--> $RUST/core/src/cell.rs
|
| pub struct Cell<T: ?Sized> {
| ^^^^
note: required because it appears within the type `mlua::state::raw::RawLua`
--> src/state/raw.rs
|
| pub struct RawLua {
| ^^^^^^
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
--> src/types/sync.rs
|
| pub(crate) struct ReentrantMutex<T>(T);
| ^^^^^^^^^^^^^^
note: required because it appears within the type `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/rc.rs
|
| struct RcBox<T: ?Sized> {
| ^^^^^
= note: required for `NonNull<rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/rc.rs
|
| pub struct Weak<
| ^^^^
note: required because it appears within the type `mlua::state::WeakLua`
--> src/state.rs
|
| pub(crate) struct WeakLua(XWeak<ReentrantMutex<RawLua>>);
| ^^^^^^^
note: required because it appears within the type `mlua::types::ValueRef`
--> src/types.rs
|
| pub(crate) struct ValueRef {
| ^^^^^^^^
note: required because it appears within the type `LuaTable`
--> src/table.rs
|
| pub struct Table(pub(crate) ValueRef);
| ^^^^^
note: required because it's used within this closure
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ^^^^^^^
note: required by a bound in `std::panic::catch_unwind`
--> $RUST/std/src/panic.rs
|
| pub fn catch_unwind<F: FnOnce() -> R + UnwindSafe, R>(f: F) -> Result<R> {
| ^^^^^^^^^^ required by this bound in `catch_unwind`
error[E0277]: the type `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
--> tests/compile/ref_nounwindsafe.rs:8:18
|
8 | catch_unwind(move || table.set("a", "b").unwrap());
| ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell<mlua::state::extra::ExtraData>` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary
| |
| required by a bound introduced by this call
|
= help: the trait `RefUnwindSafe` is not implemented for `UnsafeCell<mlua::state::extra::ExtraData>`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe`
= note: required for `Rc<UnsafeCell<mlua::state::extra::ExtraData>>` to implement `RefUnwindSafe`
note: required because it appears within the type `mlua::state::raw::RawLua`
--> src/state/raw.rs
|
| pub struct RawLua {
| ^^^^^^
note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>`
--> src/types/sync.rs
|
| pub(crate) struct ReentrantMutex<T>(T);
| ^^^^^^^^^^^^^^
note: required because it appears within the type `rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/rc.rs
|
| struct RcBox<T: ?Sized> {
| ^^^^^
= note: required for `NonNull<rc::RcBox<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::rc::Weak<mlua::types::sync::inner::ReentrantMutex<mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/rc.rs
| pub struct RawReentrantMutex<R, G> {
| ^^^^^^^^^^^^^^^^^
note: required because it appears within the type `lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>`
--> $CARGO/lock_api-0.4.12/src/remutex.rs
|
| pub struct ReentrantMutex<R, G, T: ?Sized> {
| ^^^^^^^^^^^^^^
note: required because it appears within the type `alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| struct ArcInner<T: ?Sized> {
| ^^^^^^^^
= note: required for `NonNull<alloc::sync::ArcInner<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>>` to implement `UnwindSafe`
note: required because it appears within the type `std::sync::Weak<lock_api::remutex::ReentrantMutex<parking_lot::raw_mutex::RawMutex, parking_lot::remutex::RawThreadId, mlua::state::raw::RawLua>>`
--> $RUST/alloc/src/sync.rs
|
| pub struct Weak<
| ^^^^
+4 -8
View File
@@ -4,14 +4,10 @@ fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(move |_, t: Table| {
if let Some(old) = inner.take() {
// Access old callback `Lua`.
}
inner = Some(t);
Ok(())
})?;
let f = scope.create_function_mut(|_, t: Table| {
inner = Some(t);
Ok(())
})?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
+24 -5
View File
@@ -1,5 +1,24 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_callback_capture.rs:5:9
|
5 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
error[E0373]: closure may outlive the current function, but it borrows `inner`, which is owned by the current function
--> tests/compile/scope_callback_capture.rs:7:43
|
5 | lua.scope(|scope| {
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
6 | let mut inner: Option<Table> = None;
7 | let f = scope.create_function_mut(|_, t: Table| {
| ^^^^^^^^^^^^^ may outlive borrowed value `inner`
8 | inner = Some(t);
| ----- `inner` is borrowed here
|
note: function requires argument type to outlive `'1`
--> tests/compile/scope_callback_capture.rs:7:17
|
7 | let f = scope.create_function_mut(|_, t: Table| {
| _________________^
8 | | inner = Some(t);
9 | | Ok(())
10 | | })?;
| |__________^
help: to force the closure to take ownership of `inner` (and any other referenced variables), use the `move` keyword
|
7 | let f = scope.create_function_mut(move |_, t: Table| {
| ++++
-15
View File
@@ -1,15 +0,0 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
lua.scope(|scope| {
let mut inner: Option<Table> = None;
let f = scope
.create_function_mut(|_, t: Table| {
inner = Some(t);
Ok(())
})?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
@@ -1,5 +0,0 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_callback_inner.rs:5:9
|
5 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
-15
View File
@@ -1,15 +0,0 @@
use mlua::{Lua, Table};
fn main() {
let lua = Lua::new();
let mut outer: Option<Table> = None;
lua.scope(|scope| {
let f = scope
.create_function_mut(|_, t: Table| {
outer = Some(t);
Ok(())
})?;
f.call::<()>(lua.create_table()?)?;
Ok(())
});
}
@@ -1,5 +0,0 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_callback_outer.rs:6:9
|
6 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
+5 -6
View File
@@ -10,12 +10,11 @@ fn main() {
let f = {
let mut test = Test { field: 0 };
scope
.create_function_mut(|_, ()| {
test.field = 42;
//~^ error: `test` does not live long enough
Ok(())
})?
scope.create_function_mut(|_, ()| {
test.field = 42;
//~^ error: `test` does not live long enough
Ok(())
})?
};
f.call::<()>(())
+24 -5
View File
@@ -1,5 +1,24 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_invariance.rs:9:9
|
9 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
error[E0373]: closure may outlive the current function, but it borrows `test.field`, which is owned by the current function
--> tests/compile/scope_invariance.rs:13:39
|
9 | lua.scope(|scope| {
| ----- has type `&'1 mut mlua::scope::Scope<'1, '_>`
...
13 | scope.create_function_mut(|_, ()| {
| ^^^^^^^ may outlive borrowed value `test.field`
14 | test.field = 42;
| ---------- `test.field` is borrowed here
|
note: function requires argument type to outlive `'1`
--> tests/compile/scope_invariance.rs:13:13
|
13 | / scope.create_function_mut(|_, ()| {
14 | | test.field = 42;
15 | | //~^ error: `test` does not live long enough
16 | | Ok(())
17 | | })?
| |______________^
help: to force the closure to take ownership of `test.field` (and any other referenced variables), use the `move` keyword
|
13 | scope.create_function_mut(move |_, ()| {
| ++++
+3 -3
View File
@@ -2,14 +2,14 @@ use mlua::{Lua, UserData};
fn main() {
struct MyUserData<'a>(&'a mut i32);
impl<'a> UserData for MyUserData<'a> {}
impl UserData for MyUserData<'_> {}
let mut i = 1;
let lua = Lua::new();
lua.scope(|scope| {
let _a = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_nonstatic_userdata(MyUserData(&mut i)).unwrap();
let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
Ok(())
});
}
+10 -3
View File
@@ -1,5 +1,12 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_mutable_aliasing.rs:10:9
error[E0499]: cannot borrow `i` as mutable more than once at a time
--> tests/compile/scope_mutable_aliasing.rs:12:51
|
10 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
11 | let _a = scope.create_userdata(MyUserData(&mut i)).unwrap();
| -----------------------------------------
| | |
| | first mutable borrow occurs here
| argument requires that `i` is borrowed for `'1`
12 | let _b = scope.create_userdata(MyUserData(&mut i)).unwrap();
| ^^^^^^ second mutable borrow occurs here
+3 -3
View File
@@ -3,16 +3,16 @@ use mlua::{Lua, UserData};
fn main() {
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData<'a>(&'a i32);
impl<'a> UserData for MyUserData<'a> {}
impl UserData for MyUserData<'_> {}
let igood = 1;
let lua = Lua::new();
lua.scope(|scope| {
let _ugood = scope.create_nonstatic_userdata(MyUserData(&igood)).unwrap();
let _ugood = scope.create_userdata(MyUserData(&igood)).unwrap();
let _ubad = {
let ibad = 42;
scope.create_nonstatic_userdata(MyUserData(&ibad)).unwrap();
scope.create_userdata(MyUserData(&ibad)).unwrap();
};
Ok(())
});
+13 -3
View File
@@ -1,5 +1,15 @@
error[E0599]: no method named `scope` found for struct `Lua` in the current scope
--> tests/compile/scope_userdata_borrow.rs:11:9
error[E0597]: `ibad` does not live long enough
--> tests/compile/scope_userdata_borrow.rs:15:46
|
11 | lua.scope(|scope| {
| ----^^^^^ method not found in `Lua`
| ----- has type `&mut mlua::scope::Scope<'_, '1>`
...
14 | let ibad = 42;
| ---- binding `ibad` declared here
15 | scope.create_userdata(MyUserData(&ibad)).unwrap();
| ---------------------------------^^^^^--
| | |
| | borrowed value does not live long enough
| argument requires that `ibad` is borrowed for `'1`
16 | };
| - `ibad` dropped here while still borrowed
-19
View File
@@ -1,19 +0,0 @@
use mlua::{AnyUserData, Lua, Table, UserData, Result};
fn main() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
// Should not allow userdata borrow to outlive lifetime of AnyUserData handle
struct MyUserData;
impl UserData for MyUserData {};
let _userdata_ref;
{
let touter = globals.get::<Table>("touter")?;
touter.set("userdata", lua.create_userdata(MyUserData)?)?;
let userdata = touter.get::<AnyUserData>("userdata")?;
_userdata_ref = userdata.borrow::<MyUserData>();
//~^ error: `userdata` does not live long enough
}
Ok(())
}
-13
View File
@@ -1,13 +0,0 @@
error[E0597]: `userdata` does not live long enough
--> $DIR/userdata_borrow.rs:15:25
|
15 | _userdata_ref = userdata.borrow::<MyUserData>();
| ^^^^^^^^ borrowed value does not live long enough
16 | //~^ error: `userdata` does not live long enough
17 | }
| - `userdata` dropped here while still borrowed
18 | Ok(())
19 | }
| - borrow might be used here, when `_userdata_ref` is dropped and runs the destructor for type `std::result::Result<std::cell::Ref<'_, main::MyUserData>, mlua::error::Error>`
|
= note: values in a scope are dropped in the opposite order they are defined
+90 -150
View File
@@ -1,11 +1,10 @@
use std::cell::Cell;
use std::rc::Rc;
use std::string::String as StdString;
use std::sync::Arc;
use mlua::{
AnyUserData, Error, Function, Lua, MetaMethod, Result, String, UserData, UserDataFields,
UserDataMethods,
AnyUserData, Error, Function, Lua, MetaMethod, ObjectLike, Result, String, UserData, UserDataFields,
UserDataMethods, UserDataRegistry,
};
#[test]
@@ -14,20 +13,20 @@ fn test_scope_func() -> Result<()> {
let rc = Rc::new(Cell::new(0));
lua.scope(|scope| {
let r = rc.clone();
let rc2 = rc.clone();
let f = scope.create_function(move |_, ()| {
r.set(42);
rc2.set(42);
Ok(())
})?;
lua.globals().set("bad", f.clone())?;
f.call::<_, ()>(())?;
lua.globals().set("f", &f)?;
f.call::<()>(())?;
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
assert_eq!(rc.get(), 42);
assert_eq!(Rc::strong_count(&rc), 1);
match lua.globals().get::<_, Function>("bad")?.call::<_, ()>(()) {
match lua.globals().get::<Function>("f")?.call::<()>(()) {
Err(Error::CallbackError { ref cause, .. }) => match *cause.as_ref() {
Error::CallbackDestructed => {}
ref err => panic!("wrong error type {:?}", err),
@@ -49,7 +48,7 @@ fn test_scope_capture() -> Result<()> {
i = 42;
Ok(())
})?
.call::<_, ()>(())
.call::<()>(())
})?;
assert_eq!(i, 42);
@@ -61,12 +60,8 @@ fn test_scope_outer_lua_access() -> Result<()> {
let lua = Lua::new();
let table = lua.create_table()?;
lua.scope(|scope| {
scope
.create_function_mut(|_, ()| table.set("a", "b"))?
.call::<_, ()>(())
})?;
assert_eq!(table.get::<_, String>("a")?, "b");
lua.scope(|scope| scope.create_function(|_, ()| table.set("a", "b"))?.call::<()>(()))?;
assert_eq!(table.get::<String>("a")?, "b");
Ok(())
}
@@ -75,11 +70,11 @@ fn test_scope_outer_lua_access() -> Result<()> {
fn test_scope_userdata_fields() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) {
fields.add_field("field", "hello");
fields.add_field_method_get("val", |_, data| Ok(data.0.get()));
fields.add_field_method_set("val", |_, data, val| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_field("field", "hello");
reg.add_field_method_get("val", |_, data| Ok(data.0.get()));
reg.add_field_method_set("val", |_, data, val| {
data.0.set(val);
Ok(())
});
@@ -101,7 +96,7 @@ fn test_scope_userdata_fields() -> Result<()> {
)
.eval()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&i))?))?;
assert_eq!(i.get(), 44);
@@ -112,14 +107,14 @@ fn test_scope_userdata_fields() -> Result<()> {
fn test_scope_userdata_methods() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
methods.add_method("dec", |_, data, ()| {
reg.add_method("dec", |_, data, ()| {
data.0.set(data.0.get() - 1);
Ok(())
});
@@ -142,7 +137,7 @@ fn test_scope_userdata_methods() -> Result<()> {
)
.eval()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&i))?))?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&i))?))?;
assert_eq!(i.get(), 44);
@@ -150,19 +145,19 @@ fn test_scope_userdata_methods() -> Result<()> {
}
#[test]
fn test_scope_userdata_functions() -> Result<()> {
fn test_scope_userdata_ops() -> Result<()> {
struct MyUserData<'a>(&'a i64);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_meta_method(MetaMethod::Add, |lua, this, ()| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_meta_method(MetaMethod::Add, |lua, this, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
globals.set("i", globals.get::<i64>("i")? + this.0)?;
Ok(())
});
methods.add_meta_method(MetaMethod::Sub, |lua, this, ()| {
reg.add_meta_method(MetaMethod::Sub, |lua, this, ()| {
let globals = lua.globals();
globals.set("i", globals.get::<_, i64>("i")? + this.0)?;
globals.set("i", globals.get::<i64>("i")? + this.0)?;
Ok(())
});
}
@@ -184,9 +179,34 @@ fn test_scope_userdata_functions() -> Result<()> {
)
.eval::<Function>()?;
lua.scope(|scope| f.call::<_, ()>(scope.create_nonstatic_userdata(MyUserData(&dummy))?))?;
lua.scope(|scope| f.call::<()>(scope.create_userdata(MyUserData(&dummy))?))?;
assert_eq!(lua.globals().get::<_, i64>("i")?, 3);
assert_eq!(lua.globals().get::<i64>("i")?, 3);
Ok(())
}
#[test]
fn test_scope_userdata_values() -> Result<()> {
struct MyUserData<'a>(&'a i64);
impl UserData for MyUserData<'_> {
fn register(registry: &mut UserDataRegistry<Self>) {
registry.add_method("get", |_, data, ()| Ok(*data.0));
}
}
let lua = Lua::new();
let i = 42;
let data = MyUserData(&i);
lua.scope(|scope| {
let ud = scope.create_userdata(data)?;
assert_eq!(ud.call_method::<i64>("get", &ud)?, 42);
ud.set_user_value("user_value")?;
assert_eq!(ud.user_value::<String>()?, "user_value");
Ok(())
})?;
Ok(())
}
@@ -196,8 +216,8 @@ fn test_scope_userdata_mismatch() -> Result<()> {
struct MyUserData<'a>(&'a Cell<i64>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
@@ -208,13 +228,7 @@ fn test_scope_userdata_mismatch() -> Result<()> {
lua.load(
r#"
function okay(a, b)
a.inc(a)
b.inc(b)
end
function bad(a, b)
a.inc(b)
end
function inc(a, b) a.inc(b) end
"#,
)
.exec()?;
@@ -222,29 +236,22 @@ fn test_scope_userdata_mismatch() -> Result<()> {
let a = Cell::new(1);
let b = Cell::new(1);
let okay: Function = lua.globals().get("okay")?;
let bad: Function = lua.globals().get("bad")?;
let inc: Function = lua.globals().get("inc")?;
lua.scope(|scope| {
let au = scope.create_nonstatic_userdata(MyUserData(&a))?;
let bu = scope.create_nonstatic_userdata(MyUserData(&b))?;
assert!(okay.call::<_, ()>((au.clone(), bu.clone())).is_ok());
match bad.call::<_, ()>((au, bu)) {
let au = scope.create_userdata(MyUserData(&a))?;
let bu = scope.create_userdata(MyUserData(&b))?;
assert!(inc.call::<()>((&au, &au)).is_ok());
match inc.call::<()>((&au, &bu)) {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::BadArgument {
to,
pos,
name,
cause,
} => {
Error::BadArgument { to, pos, name, cause } => {
assert_eq!(to.as_deref(), Some("MyUserData.inc"));
assert_eq!(*pos, 1);
assert_eq!(name.as_deref(), Some("self"));
assert!(matches!(*cause.as_ref(), Error::UserDataTypeMismatch));
}
other => panic!("wrong error type {:?}", other),
other => panic!("wrong error type {other:?}"),
},
Err(other) => panic!("wrong error type {:?}", other),
Err(other) => panic!("wrong error type {other:?}"),
Ok(_) => panic!("incorrectly returned Ok"),
}
Ok(())
@@ -257,114 +264,46 @@ fn test_scope_userdata_mismatch() -> Result<()> {
fn test_scope_userdata_drop() -> Result<()> {
let lua = Lua::new();
struct MyUserData(#[allow(unused)] Rc<()>);
struct MyUserData<'a>(&'a Cell<i64>, #[allow(unused)] Rc<()>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("method", |_, _, ()| Ok(()));
}
}
struct MyUserDataArc(#[allow(unused)] Arc<()>);
impl UserData for MyUserDataArc {}
let rc = Rc::new(());
let arc = Arc::new(());
lua.scope(|scope| {
let ud = scope.create_userdata(MyUserData(rc.clone()))?;
ud.set_user_value(MyUserDataArc(arc.clone()))?;
lua.globals().set("ud", ud)?;
assert_eq!(Rc::strong_count(&rc), 2);
assert_eq!(Arc::strong_count(&arc), 2);
Ok(())
})?;
lua.gc_collect()?;
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(Arc::strong_count(&arc), 1);
match lua.load("ud:method()").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected CallbackDestructed, got {:?}", err),
},
r => panic!("improper return for destructed userdata: {:?}", r),
};
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
match ud.borrow::<MyUserData>() {
Ok(_) => panic!("succesfull borrow for destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
}
match ud.get_metatable() {
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!(
"improper metatable error for destructed userdata: {:?}",
err
),
}
Ok(())
}
#[test]
fn test_scope_nonstatic_userdata_drop() -> Result<()> {
let lua = Lua::new();
struct MyUserData<'a>(&'a Cell<i64>, #[allow(unused)] Arc<()>);
impl<'a> UserData for MyUserData<'a> {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
impl UserData for MyUserData<'_> {
fn register(reg: &mut UserDataRegistry<Self>) {
reg.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
});
}
}
struct MyUserDataArc(#[allow(unused)] Arc<()>);
impl UserData for MyUserDataArc {}
let i = Cell::new(1);
let arc = Arc::new(());
let (i, rc) = (Cell::new(1), Rc::new(()));
lua.scope(|scope| {
let ud = scope.create_nonstatic_userdata(MyUserData(&i, arc.clone()))?;
ud.set_user_value(MyUserDataArc(arc.clone()))?;
let ud = scope.create_userdata(MyUserData(&i, rc.clone()))?;
lua.globals().set("ud", ud)?;
lua.load("ud:inc()").exec()?;
assert_eq!(Arc::strong_count(&arc), 3);
assert_eq!(Rc::strong_count(&rc), 2);
Ok(())
})?;
lua.gc_collect()?;
assert_eq!(Arc::strong_count(&arc), 1);
assert_eq!(Rc::strong_count(&rc), 1);
assert_eq!(i.get(), 2);
match lua.load("ud:inc()").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected CallbackDestructed, got {:?}", err),
Error::UserDataDestructed => {}
err => panic!("expected UserDataDestructed, got {err:?}"),
},
r => panic!("improper return for destructed userdata: {:?}", r),
r => panic!("improper return for destructed userdata: {r:?}"),
};
let ud = lua.globals().get::<_, AnyUserData>("ud")?;
match ud.borrow::<MyUserData>() {
let ud = lua.globals().get::<AnyUserData>("ud")?;
match ud.borrow_scoped::<MyUserData, _>(|_| Ok::<_, Error>(())) {
Ok(_) => panic!("succesfull borrow for destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!("improper borrow error for destructed userdata: {:?}", err),
Err(err) => panic!("improper borrow error for destructed userdata: {err:?}"),
}
match ud.get_metatable() {
Ok(_) => panic!("successful metatable retrieval of destructed userdata"),
Err(Error::UserDataDestructed) => {}
Err(err) => panic!(
"improper metatable error for destructed userdata: {:?}",
err
),
Err(err) => panic!("improper metatable error for destructed userdata: {err:?}"),
}
Ok(())
@@ -377,7 +316,7 @@ fn test_scope_userdata_ref() -> Result<()> {
struct MyUserData(Cell<i64>);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method("inc", |_, data, ()| {
data.0.set(data.0.get() + 1);
Ok(())
@@ -407,7 +346,7 @@ fn test_scope_userdata_ref_mut() -> Result<()> {
struct MyUserData(i64);
impl UserData for MyUserData {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("inc", |_, data, ()| {
data.0 += 1;
Ok(())
@@ -438,8 +377,9 @@ fn test_scope_any_userdata() -> Result<()> {
reg.add_meta_method("__tostring", |_, data, ()| Ok(data.clone()));
})?;
let data = StdString::from("foo");
lua.scope(|scope| {
let ud = scope.create_any_userdata(StdString::from("foo"))?;
let ud = scope.create_any_userdata_ref(&data)?;
lua.globals().set("ud", ud)?;
lua.load("assert(tostring(ud) == 'foo')").exec()
})?;
@@ -447,10 +387,10 @@ fn test_scope_any_userdata() -> Result<()> {
// Check that userdata is destructed
match lua.load("tostring(ud)").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected CallbackDestructed, got {:?}", err),
Error::UserDataDestructed => {}
err => panic!("expected CallbackDestructed, got {err:?}"),
},
r => panic!("improper return for destructed userdata: {:?}", r),
r => panic!("improper return for destructed userdata: {r:?}"),
};
Ok(())
@@ -495,7 +435,7 @@ fn modify_userdata(lua: &Lua, ud: AnyUserData) -> Result<()> {
)
.eval()?;
f.call(ud)?;
f.call::<()>(ud)?;
Ok(())
}
-44
View File
@@ -71,50 +71,6 @@ fn test_serialize() -> Result<(), Box<dyn StdError>> {
Ok(())
}
// #[test]
// fn test_serialize_in_scope() -> LuaResult<()> {
// #[derive(Serialize, Clone)]
// struct MyUserData(i64, String);
// impl UserData for MyUserData {}
// let lua = Lua::new();
// lua.scope(|scope| {
// let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
// assert_eq!(
// serde_json::to_value(&ud).unwrap(),
// serde_json::json!((-5, "test userdata"))
// );
// Ok(())
// })?;
// lua.scope(|scope| {
// let ud = scope.create_ser_userdata(MyUserData(-5, "test userdata".into()))?;
// lua.globals().set("ud", ud)
// })?;
// let val = lua.load("ud").eval::<Value>()?;
// match serde_json::to_value(&val) {
// Ok(v) => panic!("expected destructed error, got {}", v),
// Err(e) if e.to_string().contains("destructed") => {}
// Err(e) => panic!("expected destructed error, got {}", e),
// }
// struct MyUserDataRef<'a>(#[allow(unused)] &'a ());
// impl<'a> UserData for MyUserDataRef<'a> {}
// lua.scope(|scope| {
// let ud = scope.create_nonstatic_userdata(MyUserDataRef(&()))?;
// match serde_json::to_value(&ud) {
// Ok(v) => panic!("expected serialization error, got {}", v),
// Err(serde_json::Error { .. }) => {}
// };
// Ok(())
// })?;
// Ok(())
// }
#[test]
fn test_serialize_any_userdata() -> Result<(), Box<dyn StdError>> {
let lua = Lua::new();
+2 -2
View File
@@ -336,8 +336,8 @@ fn test_userdata_take() -> Result<()> {
}
match lua.load("userdata:num()").exec() {
Err(Error::CallbackError { ref cause, .. }) => match cause.as_ref() {
Error::CallbackDestructed => {}
err => panic!("expected `CallbackDestructed`, got {:?}", err),
Error::UserDataDestructed => {}
err => panic!("expected `UserDataDestructed`, got {:?}", err),
},
r => panic!("improper return for destructed userdata: {:?}", r),
}