From da4404baa54081e1306e05acde792b2056a0ff69 Mon Sep 17 00:00:00 2001 From: Alex Orlenko Date: Fri, 20 Sep 2024 13:01:55 +0100 Subject: [PATCH] Add `Lua::scope` back --- src/lib.rs | 2 +- src/scope.rs | 900 +++--------------- src/state.rs | 37 +- src/state/extra.rs | 4 +- src/state/raw.rs | 106 +-- src/types.rs | 4 +- src/userdata.rs | 100 +- src/userdata/cell.rs | 161 +++- src/userdata/registry.rs | 185 ++-- src/util/error.rs | 2 +- src/util/mod.rs | 5 +- src/util/userdata.rs | 44 +- tests/compile.rs | 3 - tests/compile/async_any_userdata_method.rs | 9 +- .../compile/async_any_userdata_method.stderr | 82 +- tests/compile/async_nonstatic_userdata.rs | 4 +- tests/compile/async_nonstatic_userdata.stderr | 8 +- tests/compile/async_userdata_method.rs | 14 - tests/compile/async_userdata_method.stderr | 17 - tests/compile/function_borrow.rs | 4 +- tests/compile/function_borrow.stderr | 31 +- tests/compile/lua_norefunwindsafe.stderr | 84 +- tests/compile/non_send.rs | 6 +- tests/compile/non_send.stderr | 31 +- tests/compile/ref_nounwindsafe.stderr | 129 +-- tests/compile/scope_callback_capture.rs | 12 +- tests/compile/scope_callback_capture.stderr | 29 +- tests/compile/scope_callback_inner.rs | 15 - tests/compile/scope_callback_inner.stderr | 5 - tests/compile/scope_callback_outer.rs | 15 - tests/compile/scope_callback_outer.stderr | 5 - tests/compile/scope_invariance.rs | 11 +- tests/compile/scope_invariance.stderr | 29 +- tests/compile/scope_mutable_aliasing.rs | 6 +- tests/compile/scope_mutable_aliasing.stderr | 13 +- tests/compile/scope_userdata_borrow.rs | 6 +- tests/compile/scope_userdata_borrow.stderr | 16 +- tests/compile/userdata_borrow.rs | 19 - tests/compile/userdata_borrow.stderr | 13 - tests/{scope.rs.1 => scope.rs} | 240 ++--- tests/serde.rs | 44 - tests/userdata.rs | 4 +- 42 files changed, 847 insertions(+), 1607 deletions(-) delete mode 100644 tests/compile/async_userdata_method.rs delete mode 100644 tests/compile/async_userdata_method.stderr delete mode 100644 tests/compile/scope_callback_inner.rs delete mode 100644 tests/compile/scope_callback_inner.stderr delete mode 100644 tests/compile/scope_callback_outer.rs delete mode 100644 tests/compile/scope_callback_outer.stderr delete mode 100644 tests/compile/userdata_borrow.rs delete mode 100644 tests/compile/userdata_borrow.stderr rename tests/{scope.rs.1 => scope.rs} (55%) diff --git a/src/lib.rs b/src/lib.rs index a61385e7..b6e153f7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,7 +87,7 @@ mod hook; mod luau; mod memory; mod multi; -// mod scope; +mod scope; mod state; mod stdlib; mod string; diff --git a/src/scope.rs b/src/scope.rs index 25678c90..57f6598f 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,55 +1,39 @@ -use std::any::Any; -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; use std::marker::PhantomData; use std::mem; -use std::os::raw::c_int; - -#[cfg(feature = "serialize")] -use serde::Serialize; +use std::os::raw::c_void; use crate::error::{Error, Result}; use crate::function::Function; -use crate::lua::Lua; -use crate::types::{Callback, CallbackUpvalue, MaybeSend, SubtypeId, ValueRef}; -use crate::userdata::{ - AnyUserData, MetaMethod, UserData, UserDataCell, UserDataFields, UserDataMethods, -}; -use crate::userdata_impl::UserDataRegistry; -use crate::util::{ - self, assert_stack, check_stack, init_userdata_metatable, push_string, push_table, - rawset_field, short_type_name, take_userdata, StackGuard, -}; -use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti}; - -#[cfg(feature = "lua54")] -use crate::userdata::USER_VALUE_MAXSLOT; - -#[cfg(feature = "async")] -use std::future::Future; +use crate::state::{Lua, LuaGuard, RawLua}; +use crate::types::{Callback, CallbackUpvalue, ScopedCallback, SubtypeId, ValueRef}; +use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage}; +use crate::util::{self, assert_stack, check_stack, get_userdata, take_userdata, StackGuard}; +use crate::value::{FromLuaMulti, IntoLuaMulti}; /// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and -/// callbacks that are not required to be Send or 'static. +/// callbacks that are not required to be `Send` or `'static`. /// /// See [`Lua::scope`] for more details. -/// -/// [`Lua::scope`]: crate::Lua.html::scope -pub struct Scope<'lua, 'scope> -where - 'lua: 'scope, -{ - lua: &'lua Lua, - destructors: RefCell, DestructorCallback<'lua>)>>, - _scope_invariant: PhantomData>, +pub struct Scope<'scope, 'env: 'scope> { + lua: LuaGuard, + destructors: Destructors<'env>, + _scope_invariant: PhantomData<&'scope mut &'scope ()>, + _env_invariant: PhantomData<&'env mut &'env ()>, } -type DestructorCallback<'lua> = Box) -> Vec> + 'lua>; +type DestructorCallback<'a> = Box Vec>>; -impl<'lua, 'scope> Scope<'lua, 'scope> { - pub(crate) fn new(lua: &'lua Lua) -> Scope<'lua, 'scope> { +// Implement Drop on Destructors instead of Scope to avoid compilation error +struct Destructors<'a>(RefCell)>>); + +impl<'scope, 'env: 'scope> Scope<'scope, 'env> { + pub(crate) fn new(lua: LuaGuard) -> Self { Scope { lua, - destructors: RefCell::new(Vec::new()), + destructors: Destructors(RefCell::new(Vec::new())), _scope_invariant: PhantomData, + _env_invariant: PhantomData, } } @@ -57,28 +41,16 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { /// /// This is a version of [`Lua::create_function`] that creates a callback which expires on /// scope drop. See [`Lua::scope`] for more details. - /// - /// [`Lua::create_function`]: crate::Lua::create_function - /// [`Lua::scope`]: crate::Lua::scope - pub fn create_function<'callback, A, R, F>(&'callback self, func: F) -> Result> + pub fn create_function(&'scope self, func: F) -> Result where - A: FromLuaMulti<'callback>, + F: Fn(&Lua, A) -> Result + 'scope, + A: FromLuaMulti, R: IntoLuaMulti, - F: Fn(&'callback Lua, A) -> Result + 'scope, { - // Safe, because 'scope must outlive 'callback (due to Self containing 'scope), however the - // callback itself must be 'scope lifetime, so the function should not be able to capture - // anything of 'callback lifetime. 'scope can't be shortened due to being invariant, and - // the 'callback lifetime here can't be enlarged due to coming from a universal - // quantification in Lua::scope. - // - // I hope I got this explanation right, but in any case this is tested with compiletest_rs - // to make sure callbacks can't capture handles with lifetime outside the scope, inside the - // scope, and owned inside the callback itself. unsafe { - self.create_callback(Box::new(move |lua, nargs| { - let args = A::from_stack_args(nargs, 1, None, lua)?; - func(lua, args)?.push_into_stack_multi(lua) + self.create_callback(Box::new(move |rawlua, nargs| { + let args = A::from_stack_args(nargs, 1, None, rawlua)?; + func(rawlua.lua(), args)?.push_into_stack_multi(rawlua) })) } } @@ -87,86 +59,31 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { /// /// This is a version of [`Lua::create_function_mut`] that creates a callback which expires /// on scope drop. See [`Lua::scope`] and [`Scope::create_function`] for more details. - /// - /// [`Lua::create_function_mut`]: crate::Lua::create_function_mut - /// [`Lua::scope`]: crate::Lua::scope - /// [`Scope::create_function`]: #method.create_function - pub fn create_function_mut<'callback, A, R, F>( - &'callback self, - func: F, - ) -> Result> + pub fn create_function_mut(&'scope self, func: F) -> Result where - A: FromLuaMulti<'callback>, + F: FnMut(&Lua, A) -> Result + 'scope, + A: FromLuaMulti, R: IntoLuaMulti, - F: FnMut(&'callback Lua, A) -> Result + 'scope, { let func = RefCell::new(func); self.create_function(move |lua, args| { - (*func - .try_borrow_mut() - .map_err(|_| Error::RecursiveMutCallback)?)(lua, args) + (*func.try_borrow_mut().map_err(|_| Error::RecursiveMutCallback)?)(lua, args) }) } - /// Creates a Lua userdata object from a custom userdata type. - /// - /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on - /// scope drop, and does not require that the userdata type be Send (but still requires that the - /// UserData be 'static). - /// See [`Lua::scope`] for more details. - /// - /// [`Lua::create_userdata`]: crate::Lua::create_userdata - /// [`Lua::scope`]: crate::Lua::scope - pub fn create_userdata(&self, data: T) -> Result> - where - T: UserData + 'static, - { - // Safe even though T may not be Send, because the parent Lua cannot be sent to another - // thread while the Scope is alive (or the returned AnyUserData handle even). - unsafe { - let ud = self.lua.make_userdata(UserDataCell::new(data))?; - self.seal_userdata::(&ud)?; - Ok(ud) - } - } - - /// Creates a Lua userdata object from a custom serializable userdata type. - /// - /// This is a version of [`Lua::create_ser_userdata`] that creates a userdata which expires on - /// scope drop, and does not require that the userdata type be Send (but still requires that the - /// UserData be 'static). - /// See [`Lua::scope`] for more details. - /// - /// Requires `feature = "serialize"` - /// - /// [`Lua::create_ser_userdata`]: crate::Lua::create_ser_userdata - /// [`Lua::scope`]: crate::Lua::scope - #[cfg(feature = "serialize")] - #[cfg_attr(docsrs, doc(cfg(feature = "serialize")))] - pub fn create_ser_userdata(&self, data: T) -> Result> - where - T: UserData + Serialize + 'static, - { - unsafe { - let ud = self.lua.make_userdata(UserDataCell::new_ser(data))?; - self.seal_userdata::(&ud)?; - Ok(ud) - } - } - /// Creates a Lua userdata object from a reference to custom userdata type. /// /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on - /// scope drop, and does not require that the userdata type be Send. This method takes non-'static - /// reference to the data. See [`Lua::scope`] for more details. + /// scope drop, and does not require that the userdata type be Send. This method takes + /// non-'static reference to the data. See [`Lua::scope`] for more details. /// /// Userdata created with this method will not be able to be mutated from Lua. - pub fn create_userdata_ref(&self, data: &'scope T) -> Result> + pub fn create_userdata_ref(&'scope self, data: &'env T) -> Result where T: UserData + 'static, { unsafe { - let ud = self.lua.make_userdata(UserDataCell::new_ref(data))?; + let ud = self.lua.make_userdata(UserDataStorage::new_ref(data))?; self.seal_userdata::(&ud)?; Ok(ud) } @@ -175,31 +92,14 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { /// Creates a Lua userdata object from a mutable reference to custom userdata type. /// /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on - /// scope drop, and does not require that the userdata type be Send. This method takes non-'static - /// mutable reference to the data. See [`Lua::scope`] for more details. - pub fn create_userdata_ref_mut(&self, data: &'scope mut T) -> Result> + /// scope drop, and does not require that the userdata type be Send. This method takes + /// non-'static mutable reference to the data. See [`Lua::scope`] for more details. + pub fn create_userdata_ref_mut(&'scope self, data: &'env mut T) -> Result where T: UserData + 'static, { unsafe { - let ud = self.lua.make_userdata(UserDataCell::new_ref_mut(data))?; - self.seal_userdata::(&ud)?; - Ok(ud) - } - } - - /// Creates a Lua userdata object from a custom Rust type. - /// - /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on - /// scope drop and does not require that the userdata type be Send (but still requires that the - /// UserData be 'static). See [`Lua::scope`] for more details. - #[inline] - pub fn create_any_userdata(&self, data: T) -> Result> - where - T: 'static, - { - unsafe { - let ud = self.lua.make_any_userdata(UserDataCell::new(data))?; + let ud = self.lua.make_userdata(UserDataStorage::new_ref_mut(data))?; self.seal_userdata::(&ud)?; Ok(ud) } @@ -212,12 +112,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { /// reference to the data. See [`Lua::scope`] for more details. /// /// Userdata created with this method will not be able to be mutated from Lua. - pub fn create_any_userdata_ref(&self, data: &'scope T) -> Result> + pub fn create_any_userdata_ref(&'scope self, data: &'env T) -> Result where T: 'static, { unsafe { - let ud = self.lua.make_any_userdata(UserDataCell::new_ref(data))?; + let ud = self.lua.make_any_userdata(UserDataStorage::new_ref(data))?; self.seal_userdata::(&ud)?; Ok(ud) } @@ -228,301 +128,73 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { /// This is a version of [`Lua::create_any_userdata`] that creates a userdata which expires on /// scope drop, and does not require that the Rust type be Send. This method takes non-'static /// mutable reference to the data. See [`Lua::scope`] for more details. - pub fn create_any_userdata_ref_mut(&self, data: &'scope mut T) -> Result> + pub fn create_any_userdata_ref_mut(&'scope self, data: &'env mut T) -> Result where T: 'static, { - let lua = self.lua; unsafe { - let ud = lua.make_any_userdata(UserDataCell::new_ref_mut(data))?; + let ud = self.lua.make_any_userdata(UserDataStorage::new_ref_mut(data))?; self.seal_userdata::(&ud)?; Ok(ud) } } - /// Shortens the lifetime of a userdata to the lifetime of the scope. - unsafe fn seal_userdata(&self, ud: &AnyUserData<'lua>) -> Result<()> { - #[cfg(any(feature = "lua51", feature = "luajit"))] - let newtable = self.lua.create_table()?; - let destructor: DestructorCallback = Box::new(move |ud| { - let state = ud.lua.state(); - let _sg = StackGuard::new(state); - assert_stack(state, 2); - - // Check that userdata is not destructed (via `take()` call) - if ud.lua.push_userdata_ref(&ud).is_err() { - return vec![]; - } - - // Clear associated user values - #[cfg(feature = "lua54")] - for i in 1..=USER_VALUE_MAXSLOT { - ffi::lua_pushnil(state); - ffi::lua_setiuservalue(state, -2, i as _); - } - #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))] - { - ffi::lua_pushnil(state); - ffi::lua_setuservalue(state, -2); - } - #[cfg(any(feature = "lua51", feature = "luajit"))] - { - ud.lua.push_ref(&newtable.0); - ffi::lua_setuservalue(state, -2); - } - - vec![Box::new(take_userdata::>(state))] - }); - self.destructors - .borrow_mut() - .push((ud.0.clone(), destructor)); - - Ok(()) - } - /// Creates a Lua userdata object from a custom userdata type. /// /// This is a version of [`Lua::create_userdata`] that creates a userdata which expires on - /// scope drop, and does not require that the userdata type be Send or 'static. See + /// scope drop, and does not require that the userdata type be `Send` or `'static`. See /// [`Lua::scope`] for more details. /// - /// Lifting the requirement that the UserData type be 'static comes with some important - /// limitations, so if you only need to eliminate the Send requirement, it is probably better to - /// use [`Scope::create_userdata`] instead. - /// /// The main limitation that comes from using non-'static userdata is that the produced userdata /// will no longer have a `TypeId` associated with it, because `TypeId` can only work for - /// 'static types. This means that it is impossible, once the userdata is created, to get a + /// `'static` types. This means that it is impossible, once the userdata is created, to get a /// reference to it back *out* of an `AnyUserData` handle. This also implies that the /// "function" type methods that can be added via [`UserDataMethods`] (the ones that accept /// `AnyUserData` as a first parameter) are vastly less useful. Also, there is no way to re-use /// a single metatable for multiple non-'static types, so there is a higher cost associated with /// creating the userdata metatable each time a new userdata is created. /// - /// [`Scope::create_userdata`]: #method.create_userdata - /// [`Lua::create_userdata`]: crate::Lua::create_userdata - /// [`Lua::scope`]:crate::Lua::scope /// [`UserDataMethods`]: crate::UserDataMethods - pub fn create_nonstatic_userdata(&self, data: T) -> Result> + pub fn create_userdata(&'scope self, data: T) -> Result where - T: UserData + 'scope, + T: UserData + 'env, { - // 'callback outliving 'scope is a lie to make the types work out, required due to the - // inability to work with the more correct callback type that is universally quantified over - // 'lua. This is safe though, because `UserData::add_methods` does not get to pick the 'lua - // lifetime, so none of the static methods UserData types can add can possibly capture - // parameters. - unsafe fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>( - scope: &Scope<'lua, 'scope>, - ud_ptr: *const UserDataCell, - name: &str, - method: NonStaticMethod<'callback, T>, - ) -> Result> { - // On methods that actually receive the userdata, we fake a type check on the passed in - // userdata, where we pretend there is a unique type per call to - // `Scope::create_nonstatic_userdata`. You can grab a method from a userdata and call - // it on a mismatched userdata type, which when using normal 'static userdata will fail - // with a type mismatch, but here without this check would proceed as though you had - // called the method on the original value (since we otherwise completely ignore the - // first argument). - let func_name = format!("{}.{name}", short_type_name::()); - let check_self_type = move |lua: &Lua, nargs: c_int| -> Result<&UserDataCell> { - let state = lua.state(); - if nargs > 0 && ffi::lua_touserdata(state, -nargs) as *const _ == ud_ptr { - return Ok(&*ud_ptr); - } - Err(Error::bad_self_argument( - &func_name, - Error::UserDataTypeMismatch, - )) - }; - - match method { - NonStaticMethod::Method(method) => { - let f = Box::new(move |lua, nargs| { - let data = check_self_type(lua, nargs)?; - let data = data.try_borrow()?; - method(lua, &*data, nargs - 1) - }); - scope.create_callback(f) - } - NonStaticMethod::MethodMut(method) => { - let method = RefCell::new(method); - let f = Box::new(move |lua, nargs| { - let data = check_self_type(lua, nargs)?; - let mut method = method - .try_borrow_mut() - .map_err(|_| Error::RecursiveMutCallback)?; - let mut data = data.try_borrow_mut()?; - (*method)(lua, &mut *data, nargs - 1) - }); - scope.create_callback(f) - } - NonStaticMethod::Function(function) => scope.create_callback(function), - NonStaticMethod::FunctionMut(function) => { - let function = RefCell::new(function); - let f = Box::new(move |lua, nargs| { - let mut func = function - .try_borrow_mut() - .map_err(|_| Error::RecursiveMutCallback)?; - func(lua, nargs) - }); - scope.create_callback(f) - } - } - } - - let mut registry = NonStaticUserDataRegistry::new(); - T::add_fields(&mut registry); - T::add_methods(&mut registry); - - let lua = self.lua; - let state = lua.state(); + let state = self.lua.state(); unsafe { let _sg = StackGuard::new(state); - check_stack(state, 13)?; - - #[cfg(not(feature = "luau"))] - let ud_ptr = protect_lua!(state, 0, 1, |state| { - let ud = ffi::lua_newuserdata(state, mem::size_of::>()); - - // Set empty environment for Lua 5.1 - #[cfg(any(feature = "lua51", feature = "luajit"))] - { - ffi::lua_newtable(state); - ffi::lua_setuservalue(state, -2); - } + check_stack(state, 3)?; - ud as *const UserDataCell - })?; + // // We don't write the data to the userdata until pushing the metatable + let protect = !self.lua.unlikely_memory_error(); #[cfg(feature = "luau")] let ud_ptr = { - util::push_userdata(state, UserDataCell::new(data), true)?; - ffi::lua_touserdata(state, -1) as *const UserDataCell + let data = UserDataStorage::new_scoped(data); + util::push_userdata::>(state, data, protect)? }; - - // Prepare metatable, add meta methods first and then meta fields - let meta_methods_nrec = registry.meta_methods.len() + registry.meta_fields.len() + 1; - push_table(state, 0, meta_methods_nrec, true)?; - for (k, m) in registry.meta_methods { - lua.push(wrap_method(self, ud_ptr, &k, m)?)?; - rawset_field(state, -2, MetaMethod::validate(&k)?)?; - } - let mut has_name = false; - for (k, f) in registry.meta_fields { - has_name = has_name || k == MetaMethod::Type; - mlua_assert!(f(lua, 0)? == 1, "field function must return one value"); - rawset_field(state, -2, MetaMethod::validate(&k)?)?; - } - // Set `__name/__type` if not provided - if !has_name { - let type_name = short_type_name::(); - push_string(state, type_name.as_bytes(), !lua.unlikely_memory_error())?; - rawset_field(state, -2, MetaMethod::Type.name())?; - } - let metatable_index = ffi::lua_absindex(state, -1); - - let fields_nrec = registry.fields.len(); - if fields_nrec > 0 { - // If __index is a table then update it inplace - let index_type = ffi::lua_getfield(state, metatable_index, cstr!("__index")); - match index_type { - ffi::LUA_TNIL | ffi::LUA_TTABLE => { - if index_type == ffi::LUA_TNIL { - // Create a new table - ffi::lua_pop(state, 1); - push_table(state, 0, fields_nrec, true)?; - } - for (k, f) in registry.fields { - #[rustfmt::skip] - let NonStaticMethod::Function(f) = f else { unreachable!() }; - mlua_assert!(f(lua, 0)? == 1, "field function must return one value"); - rawset_field(state, -2, &k)?; - } - rawset_field(state, metatable_index, "__index")?; - } - _ => { - // Propagate fields to the field getters - for (k, f) in registry.fields { - registry.field_getters.push((k, f)) - } - } - } - } - - let mut field_getters_index = None; - let field_getters_nrec = registry.field_getters.len(); - if field_getters_nrec > 0 { - push_table(state, 0, field_getters_nrec, true)?; - for (k, m) in registry.field_getters { - lua.push(wrap_method(self, ud_ptr, &k, m)?)?; - rawset_field(state, -2, &k)?; - } - field_getters_index = Some(ffi::lua_absindex(state, -1)); - } - - let mut field_setters_index = None; - let field_setters_nrec = registry.field_setters.len(); - if field_setters_nrec > 0 { - push_table(state, 0, field_setters_nrec, true)?; - for (k, m) in registry.field_setters { - lua.push(wrap_method(self, ud_ptr, &k, m)?)?; - rawset_field(state, -2, &k)?; - } - field_setters_index = Some(ffi::lua_absindex(state, -1)); - } - - let mut methods_index = None; - let methods_nrec = registry.methods.len(); - if methods_nrec > 0 { - // Create table used for methods lookup - push_table(state, 0, methods_nrec, true)?; - for (k, m) in registry.methods { - lua.push(wrap_method(self, ud_ptr, &k, m)?)?; - rawset_field(state, -2, &k)?; - } - methods_index = Some(ffi::lua_absindex(state, -1)); - } - - #[cfg(feature = "luau")] - let extra_init = None; #[cfg(not(feature = "luau"))] - let extra_init: Option Result<()>> = Some(|state| { - ffi::lua_pushcfunction(state, util::userdata_destructor::>); - rawset_field(state, -2, "__gc") - }); - - init_userdata_metatable( - state, - metatable_index, - field_getters_index, - field_setters_index, - methods_index, - extra_init, - )?; - - let count = field_getters_index.map(|_| 1).unwrap_or(0) - + field_setters_index.map(|_| 1).unwrap_or(0) - + methods_index.map(|_| 1).unwrap_or(0); - ffi::lua_pop(state, count); + let ud_ptr = util::push_uninit_userdata::>(state, protect)?; + // Push the metatable and register it with no TypeId + let mut registry = UserDataRegistry::new_unique(ud_ptr as *const c_void); + T::register(&mut registry); + self.lua.push_userdata_metatable(registry)?; let mt_ptr = ffi::lua_topointer(state, -1); - // Write userdata just before attaching metatable with `__gc` metamethod + self.lua.register_userdata_metatable(mt_ptr, None); + + // Write data to the pointer and attach metatable #[cfg(not(feature = "luau"))] - std::ptr::write(ud_ptr as _, UserDataCell::new(data)); + std::ptr::write(ud_ptr, UserDataStorage::new_scoped(data)); ffi::lua_setmetatable(state, -2); - let ud = AnyUserData(lua.pop_ref(), SubtypeId::None); - lua.register_raw_userdata_metatable(mt_ptr, None); - #[cfg(any(feature = "lua51", feature = "luajit"))] - let newtable = lua.create_table()?; - let destructor: DestructorCallback = Box::new(move |ud| { - let state = ud.lua.state(); + let ud = AnyUserData(self.lua.pop_ref(), SubtypeId::None); + + let destructor: DestructorCallback = Box::new(|rawlua, vref| { + let state = rawlua.state(); let _sg = StackGuard::new(state); assert_stack(state, 2); // Check that userdata is valid (very likely) - if ud.lua.push_userdata_ref(&ud).is_err() { + if rawlua.push_userdata_ref(&vref).is_err() { return vec![]; } @@ -530,421 +202,71 @@ impl<'lua, 'scope> Scope<'lua, 'scope> { ffi::lua_getmetatable(state, -1); let mt_ptr = ffi::lua_topointer(state, -1); ffi::lua_pop(state, 1); - ud.lua.deregister_raw_userdata_metatable(mt_ptr); + rawlua.deregister_userdata_metatable(mt_ptr); - // Clear associated user values - #[cfg(feature = "lua54")] - for i in 1..=USER_VALUE_MAXSLOT { - ffi::lua_pushnil(state); - ffi::lua_setiuservalue(state, -2, i as _); - } - #[cfg(any(feature = "lua53", feature = "lua52", feature = "luau"))] - { - ffi::lua_pushnil(state); - ffi::lua_setuservalue(state, -2); - } - #[cfg(any(feature = "lua51", feature = "luajit"))] - { - ud.lua.push_ref(&newtable.0); - ffi::lua_setuservalue(state, -2); - } + let ud = take_userdata::>(state); - // A hack to drop non-static `T` - unsafe fn seal(t: T) -> Box { - let f: Box = Box::new(move || drop(t)); - mem::transmute(f) - } - - let ud = take_userdata::>(state); - vec![Box::new(seal(ud))] + vec![Box::new(move || drop(ud))] }); - self.destructors - .borrow_mut() - .push((ud.0.clone(), destructor)); + self.destructors.0.borrow_mut().push((ud.0.clone(), destructor)); Ok(ud) } } - // Unsafe, because the callback can improperly capture any value with 'callback scope, such as - // improperly capturing an argument. Since the 'callback lifetime is chosen by the user and the - // lifetime of the callback itself is 'scope (non-'static), the borrow checker will happily pick - // a 'callback that outlives 'scope to allow this. In order for this to be safe, the callback - // must NOT capture any parameters. - unsafe fn create_callback<'callback>( - &self, - f: Callback<'callback, 'scope>, - ) -> Result> { - let f = mem::transmute::, Callback<'lua, 'static>>(f); + unsafe fn create_callback(&'scope self, f: ScopedCallback<'scope>) -> Result { + let f = mem::transmute::(f); let f = self.lua.create_callback(f)?; - let destructor: DestructorCallback = Box::new(|f| { - let state = f.lua.state(); - let _sg = StackGuard::new(state); - assert_stack(state, 3); + let destructor: DestructorCallback = Box::new(|rawlua, vref| { + let ref_thread = rawlua.ref_thread(); + ffi::lua_getupvalue(ref_thread, vref.index, 1); + let upvalue = get_userdata::(ref_thread, -1); + let data = (*upvalue).data.take(); + ffi::lua_pop(ref_thread, 1); + vec![Box::new(move || drop(data))] + }); + self.destructors.0.borrow_mut().push((f.0.clone(), destructor)); - f.lua.push_ref(&f); + Ok(f) + } - // We know the destructor has not run yet because we hold a reference to the callback. + /// Shortens the lifetime of the userdata to the lifetime of the scope. + unsafe fn seal_userdata(&self, ud: &AnyUserData) -> Result<()> { + let destructor: DestructorCallback = Box::new(|rawlua, vref| { + let state = rawlua.state(); + let _sg = StackGuard::new(state); + assert_stack(state, 2); - ffi::lua_getupvalue(state, -1, 1); - let ud = take_userdata::(state); - ffi::lua_pushnil(state); - ffi::lua_setupvalue(state, -2, 1); + // Ensure that userdata is not destructed + if rawlua.push_userdata_ref(&vref).is_err() { + return vec![]; + } - vec![Box::new(ud)] + let data = take_userdata::>(state); + vec![Box::new(move || drop(data))] }); - self.destructors - .borrow_mut() - .push((f.0.clone(), destructor)); + self.destructors.0.borrow_mut().push((ud.0.clone(), destructor)); - Ok(f) + Ok(()) } } -impl<'lua, 'scope> Drop for Scope<'lua, 'scope> { +impl Drop for Destructors<'_> { fn drop(&mut self) { // We separate the action of invalidating the userdata in Lua and actually dropping the - // userdata type into two phases. This is so that, in the event a userdata drop panics, we - // can be sure that all of the userdata in Lua is actually invalidated. - - // All destructors are non-panicking, so this is fine - let to_drop = self - .destructors - .get_mut() - .drain(..) - .flat_map(|(r, dest)| dest(r)) - .collect::>(); - - drop(to_drop); - } -} - -#[allow(clippy::type_complexity)] -enum NonStaticMethod<'lua, T> { - Method(Box Result>), - MethodMut(Box Result>), - Function(Box Result>), - FunctionMut(Box Result>), -} - -struct NonStaticUserDataRegistry<'lua, T> { - // Fields - fields: Vec<(String, NonStaticMethod<'lua, T>)>, - field_getters: Vec<(String, NonStaticMethod<'lua, T>)>, - field_setters: Vec<(String, NonStaticMethod<'lua, T>)>, - meta_fields: Vec<(String, Callback<'lua, 'static>)>, - - // Methods - methods: Vec<(String, NonStaticMethod<'lua, T>)>, - meta_methods: Vec<(String, NonStaticMethod<'lua, T>)>, -} - -impl<'lua, T> NonStaticUserDataRegistry<'lua, T> { - const fn new() -> NonStaticUserDataRegistry<'lua, T> { - NonStaticUserDataRegistry { - fields: Vec::new(), - field_getters: Vec::new(), - field_setters: Vec::new(), - meta_fields: Vec::new(), - methods: Vec::new(), - meta_methods: Vec::new(), + // userdata type into two phases. This is so that, in the event a userdata drop panics, + // we can be sure that all of the userdata in Lua is actually invalidated. + + let destructors = mem::take(&mut *self.0.borrow_mut()); + if let Some(lua) = destructors.first().map(|(vref, _)| vref.lua.lock()) { + // All destructors are non-panicking, so this is fine + let to_drop = destructors + .into_iter() + .flat_map(|(vref, destructor)| destructor(&lua, vref)) + .collect::>(); + + drop(to_drop); } } } - -impl<'lua, T> UserDataFields<'lua, T> for NonStaticUserDataRegistry<'lua, T> { - fn add_field(&mut self, name: impl AsRef, value: V) - where - V: IntoLua + Clone + 'static, - { - let name = name.as_ref().to_string(); - self.fields.push(( - name, - NonStaticMethod::Function(Box::new(move |lua, _| unsafe { - value.clone().push_into_stack_multi(lua) - })), - )); - } - - fn add_field_method_get(&mut self, name: impl AsRef, method: M) - where - M: Fn(&'lua Lua, &T) -> Result + MaybeSend + 'static, - R: IntoLua, - { - let method = NonStaticMethod::Method(Box::new(move |lua, ud, _| unsafe { - method(lua, ud)?.push_into_stack_multi(lua) - })); - self.field_getters.push((name.as_ref().into(), method)); - } - - fn add_field_method_set(&mut self, name: impl AsRef, mut method: M) - where - M: FnMut(&'lua Lua, &mut T, A) -> Result<()> + MaybeSend + 'static, - A: FromLua<'lua>, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe { - let val = A::from_stack_args(nargs, 2, Some(&func_name), lua)?; - method(lua, ud, val)?.push_into_stack_multi(lua) - })); - self.field_setters.push((name.as_ref().into(), method)); - } - - fn add_field_function_get(&mut self, name: impl AsRef, function: F) - where - F: Fn(&'lua Lua, AnyUserData<'lua>) -> Result + MaybeSend + 'static, - R: IntoLua, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe { - let ud = AnyUserData::from_stack_args(nargs, 1, Some(&func_name), lua)?; - function(lua, ud)?.push_into_stack_multi(lua) - })); - self.field_getters.push((name.as_ref().into(), func)); - } - - fn add_field_function_set(&mut self, name: impl AsRef, mut function: F) - where - F: FnMut(&'lua Lua, AnyUserData<'lua>, A) -> Result<()> + MaybeSend + 'static, - A: FromLua<'lua>, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe { - let (ud, val) = <_>::from_stack_args(nargs, 1, Some(&func_name), lua)?; - function(lua, ud, val)?.push_into_stack_multi(lua) - })); - self.field_setters.push((name.as_ref().into(), func)); - } - - fn add_meta_field(&mut self, name: impl AsRef, value: V) - where - V: IntoLua + Clone + 'static, - { - let name = name.as_ref().to_string(); - let name2 = name.clone(); - self.meta_fields.push(( - name, - Box::new(move |lua, _| unsafe { - UserDataRegistry::<()>::check_meta_field(lua, &name2, value.clone())? - .push_into_stack_multi(lua) - }), - )); - } - - fn add_meta_field_with(&mut self, name: impl AsRef, f: F) - where - F: Fn(&'lua Lua) -> Result + MaybeSend + 'static, - R: IntoLua, - { - let name = name.as_ref().to_string(); - let name2 = name.clone(); - self.meta_fields.push(( - name, - Box::new(move |lua, _| unsafe { - UserDataRegistry::<()>::check_meta_field(lua, &name2, f(lua)?)? - .push_into_stack_multi(lua) - }), - )); - } -} - -impl<'lua, T> UserDataMethods<'lua, T> for NonStaticUserDataRegistry<'lua, T> { - fn add_method(&mut self, name: impl AsRef, method: M) - where - M: Fn(&'lua Lua, &T, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe { - let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?; - method(lua, ud, args)?.push_into_stack_multi(lua) - })); - self.methods.push((name.as_ref().into(), method)); - } - - fn add_method_mut(&mut self, name: impl AsRef, mut method: M) - where - M: FnMut(&'lua Lua, &mut T, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe { - let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?; - method(lua, ud, args)?.push_into_stack_multi(lua) - })); - self.methods.push((name.as_ref().into(), method)); - } - - #[cfg(feature = "async")] - fn add_async_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef, _method: M) - where - 'lua: 's, - T: 'static, - M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - MR: Future> + 's, - R: IntoLuaMulti, - { - // The panic should never happen as async non-static code wouldn't compile - // Non-static lifetime must be bounded to 'lua lifetime - panic!("asynchronous methods are not supported for non-static userdata") - } - - #[cfg(feature = "async")] - fn add_async_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef, _method: M) - where - 'lua: 's, - T: 'static, - M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - MR: Future> + 's, - R: IntoLuaMulti, - { - // The panic should never happen as async non-static code wouldn't compile - // Non-static lifetime must be bounded to 'lua lifetime - panic!("asynchronous methods are not supported for non-static userdata") - } - - fn add_function(&mut self, name: impl AsRef, function: F) - where - F: Fn(&'lua Lua, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe { - let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?; - function(lua, args)?.push_into_stack_multi(lua) - })); - self.methods.push((name.as_ref().into(), func)); - } - - fn add_function_mut(&mut self, name: impl AsRef, mut function: F) - where - F: FnMut(&'lua Lua, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe { - let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?; - function(lua, args)?.push_into_stack_multi(lua) - })); - self.methods.push((name.as_ref().into(), func)); - } - - #[cfg(feature = "async")] - fn add_async_function(&mut self, _name: impl AsRef, _function: F) - where - F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - FR: Future> + 'lua, - R: IntoLuaMulti, - { - // The panic should never happen as async non-static code wouldn't compile - // Non-static lifetime must be bounded to 'lua lifetime - panic!("asynchronous functions are not supported for non-static userdata") - } - - fn add_meta_method(&mut self, name: impl AsRef, method: M) - where - M: Fn(&'lua Lua, &T, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let method = NonStaticMethod::Method(Box::new(move |lua, ud, nargs| unsafe { - let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?; - method(lua, ud, args)?.push_into_stack_multi(lua) - })); - self.meta_methods.push((name.as_ref().into(), method)); - } - - fn add_meta_method_mut(&mut self, name: impl AsRef, mut method: M) - where - M: FnMut(&'lua Lua, &mut T, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let method = NonStaticMethod::MethodMut(Box::new(move |lua, ud, nargs| unsafe { - let args = A::from_stack_args(nargs, 2, Some(&func_name), lua)?; - method(lua, ud, args)?.push_into_stack_multi(lua) - })); - self.meta_methods.push((name.as_ref().into(), method)); - } - - #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] - fn add_async_meta_method<'s, M, A, MR, R>(&mut self, _name: impl AsRef, _method: M) - where - 'lua: 's, - T: 'static, - M: Fn(&'lua Lua, &'s T, A) -> MR + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - MR: Future> + 's, - R: IntoLuaMulti, - { - // The panic should never happen as async non-static code wouldn't compile - // Non-static lifetime must be bounded to 'lua lifetime - panic!("asynchronous meta methods are not supported for non-static userdata") - } - - #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] - fn add_async_meta_method_mut<'s, M, A, MR, R>(&mut self, _name: impl AsRef, _method: M) - where - 'lua: 's, - T: 'static, - M: Fn(&'lua Lua, &'s mut T, A) -> MR + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - MR: Future> + 's, - R: IntoLuaMulti, - { - // The panic should never happen as async non-static code wouldn't compile - // Non-static lifetime must be bounded to 'lua lifetime - panic!("asynchronous meta methods are not supported for non-static userdata") - } - - fn add_meta_function(&mut self, name: impl AsRef, function: F) - where - F: Fn(&'lua Lua, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let func = NonStaticMethod::Function(Box::new(move |lua, nargs| unsafe { - let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?; - function(lua, args)?.push_into_stack_multi(lua) - })); - self.meta_methods.push((name.as_ref().into(), func)); - } - - fn add_meta_function_mut(&mut self, name: impl AsRef, mut function: F) - where - F: FnMut(&'lua Lua, A) -> Result + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - R: IntoLuaMulti, - { - let func_name = format!("{}.{}", short_type_name::(), name.as_ref()); - let func = NonStaticMethod::FunctionMut(Box::new(move |lua, nargs| unsafe { - let args = A::from_stack_args(nargs, 1, Some(&func_name), lua)?; - function(lua, args)?.push_into_stack_multi(lua) - })); - self.meta_methods.push((name.as_ref().into(), func)); - } - - #[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))] - fn add_async_meta_function(&mut self, _name: impl AsRef, _function: F) - where - F: Fn(&'lua Lua, A) -> FR + MaybeSend + 'static, - A: FromLuaMulti<'lua>, - FR: Future> + 'lua, - R: IntoLuaMulti, - { - // The panic should never happen as async non-static code wouldn't compile - // Non-static lifetime must be bounded to 'lua lifetime - panic!("asynchronous meta functions are not supported for non-static userdata") - } -} diff --git a/src/state.rs b/src/state.rs index abde1365..2c93cc31 100644 --- a/src/state.rs +++ b/src/state.rs @@ -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(&self, f: impl FnOnce(&mut UserDataRegistry)) -> Result<()> { - let mut registry = const { UserDataRegistry::new() }; + let type_id = TypeId::of::(); + 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::(); - 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::(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, - // ) -> Result - // where - // 'lua: 'scope, - // { - // f(&Scope::new(self)) - // } + pub fn scope<'env, R>( + &self, + f: impl for<'scope> FnOnce(&'scope mut Scope<'scope, 'env>) -> Result, + ) -> Result { + 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. diff --git a/src/state/extra.rs b/src/state/extra.rs index 5b2f8c08..468f35f1 100644 --- a/src/state/extra.rs +++ b/src/state/extra.rs @@ -35,7 +35,7 @@ pub(crate) struct ExtraData { pub(super) weak: MaybeUninit, pub(super) owned: bool, - pub(super) registered_userdata: FxHashMap, + pub(super) registered_userdata_t: FxHashMap, pub(super) registered_userdata_mt: FxHashMap<*const c_void, Option>, pub(super) last_checked_userdata_mt: (*const c_void, Option), @@ -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()))), diff --git a/src/state/raw.rs b/src/state/raw.rs index 0e62140b..ad042e5e 100644 --- a/src/state/raw.rs +++ b/src/state/raw.rs @@ -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(&self, data: UserDataVariant) -> Result + pub(crate) unsafe fn make_userdata(&self, data: UserDataStorage) -> Result where T: UserData + 'static, { self.make_userdata_with_metatable(data, || { // Check if userdata/metatable is already registered let type_id = TypeId::of::(); - 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(&self, data: UserDataVariant) -> Result + pub(crate) unsafe fn make_any_userdata(&self, data: UserDataStorage) -> Result where T: 'static, { self.make_userdata_with_metatable(data, || { // Check if userdata/metatable is already registered let type_id = TypeId::of::(); - 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::(registry) + let registry = UserDataRegistry::::new(type_id); + self.create_userdata_metatable(registry) }) } unsafe fn make_userdata_with_metatable( &self, - data: UserDataVariant, + data: UserDataStorage, get_metatable_id: impl FnOnce() -> Result, ) -> Result { 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( + pub(crate) unsafe fn create_userdata_metatable( &self, - mut registry: UserDataRegistry, + registry: UserDataRegistry, ) -> Result { 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(&self, mut registry: UserDataRegistry) -> 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 Result<()>> = Some(|state| { - ffi::lua_pushcfunction(state, crate::util::userdata_destructor::>); + ffi::lua_pushcfunction(state, crate::util::userdata_destructor::>); 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::(); - (*self.extra.get()).registered_userdata.insert(type_id, id); - (*self.extra.get()) - .registered_userdata_mt - .insert(mt_ptr, Some(type_id)); + Ok(()) + } - Ok(id as Integer) + #[inline(always)] + pub(crate) unsafe fn register_userdata_metatable(&self, mt_ptr: *const c_void, type_id: Option) { + (*self.extra.get()).registered_userdata_mt.insert(mt_ptr, type_id); } - // #[inline] - // pub(crate) unsafe fn register_raw_userdata_metatable( - // &self, - // ptr: *const c_void, - // type_id: Option, - // ) { - // (*self.extra.get()) - // .registered_userdata_mt - // .insert(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(&self, idx: c_int) -> Result> { - // let guard = self.lua().lock_arc(); - // (*get_userdata::>(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 { - // 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::(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)?; diff --git a/src/types.rs b/src/types.rs index 3f0d31d2..a62e56de 100644 --- a/src/types.rs +++ b/src/types.rs @@ -52,12 +52,14 @@ pub(crate) type Callback = Box Result + Send + #[cfg(not(feature = "send"))] pub(crate) type Callback = Box Result + 'static>; +pub(crate) type ScopedCallback<'s> = Box Result + 's>; + pub(crate) struct Upvalue { pub(crate) data: T, pub(crate) extra: XRc>, } -pub(crate) type CallbackUpvalue = Upvalue; +pub(crate) type CallbackUpvalue = Upvalue>; #[cfg(all(feature = "async", feature = "send"))] pub(crate) type AsyncCallback = diff --git a/src/userdata.rs b/src/userdata.rs index f28b96a6..5b7ca723 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -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(&self) -> bool { - self.inspect::(|_, _| Ok(())).is_ok() + self.inspect::(|_| 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(&self) -> Result> { - 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(&self, f: impl FnOnce(&T) -> R) -> Result { + 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(&self) -> Result> { - 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(&self, f: impl FnOnce(&mut T) -> R) -> Result { + 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::() => { // Try to borrow userdata exclusively - let _ = (*get_userdata::>(state, -1)).try_borrow_mut()?; - take_userdata::>(state).into_inner() + let _ = (*get_userdata::>(state, -1)).try_borrow_mut()?; + take_userdata::>(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::>(lua.ref_thread(), self.0.index); - match ud { - UserDataVariant::Serializable(..) => Result::Ok(true), - _ => Result::Ok(false), - } + let ud = &*get_userdata::>(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 + pub(crate) fn inspect(&self, func: F) -> Result where T: 'static, - F: FnOnce(&'a UserDataVariant, LuaGuard) -> Result, + F: FnOnce(&UserDataStorage) -> Result, { 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::() => { - let ref_thread = lua.ref_thread(); - let ud = get_userdata::>(ref_thread, self.0.index); - func(&*ud, lua) + let ud = get_userdata::>(lua.ref_thread(), self.0.index); + func(&*ud) } _ => Err(Error::UserDataTypeMismatch), } @@ -1041,13 +1024,6 @@ impl AsRef 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::>(lua.ref_thread(), self.0.index); + let ud = &*get_userdata::>(lua.ref_thread(), self.0.index); ud.serialize(serializer) } } diff --git a/src/userdata/cell.rs b/src/userdata/cell.rs index 10313693..6df36126 100644 --- a/src/userdata/cell.rs +++ b/src/userdata/cell.rs @@ -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 { + Owned(UserDataVariant), + Scoped(ScopedUserDataVariant), +} + // A enum for storing userdata values. // It's stored inside a Lua VM and protected by the outer `ReentrantMutex`. pub(crate) enum UserDataVariant { @@ -42,39 +47,34 @@ impl Clone for UserDataVariant { } impl UserDataVariant { - #[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> { + fn try_borrow(&self) -> Result> { UserDataBorrowRef::try_from(self) } // Immutably borrows the wrapped value and returns an owned reference. #[inline(always)] - pub(crate) fn try_borrow_owned(&self) -> Result> { + fn try_borrow_owned(&self) -> Result> { UserDataRef::try_from(self.clone()) } // Mutably borrows the wrapped value in-place. #[inline(always)] - pub(crate) fn try_borrow_mut(&self) -> Result> { + fn try_borrow_mut(&self) -> Result> { 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> { + fn try_borrow_owned_mut(&self) -> Result> { 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 { + fn into_inner(self) -> Result { if !self.raw_lock().try_lock_exclusive() { return Err(Error::UserDataBorrowMutError); } @@ -108,20 +108,10 @@ impl UserDataVariant { } #[cfg(feature = "serialize")] -impl UserDataVariant { - #[inline(always)] - pub(crate) fn new_ser(data: T) -> Self { - let data = Box::new(data) as Box; - Self::Serializable(XRc::new(UserDataCell::new(data))) - } -} - -#[cfg(feature = "serialize")] -impl Serialize for UserDataVariant<()> { +impl Serialize for UserDataStorage<()> { fn serialize(&self, serializer: S) -> std::result::Result { match self { - Self::Default(_) => Err(serde::ser::Error::custom("cannot serialize ")), - 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 ")), } } } @@ -145,7 +136,7 @@ unsafe impl Sync for UserDataCell {} impl UserDataCell { #[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 FromLua for UserDataRef { let type_id = lua.get_userdata_type_id(idx)?; match type_id { Some(type_id) if type_id == TypeId::of::() => { - (*get_userdata::>(lua.state(), idx)).try_borrow_owned() + (*get_userdata::>(lua.state(), idx)).try_borrow_owned() } _ => Err(Error::UserDataTypeMismatch), } @@ -275,7 +266,7 @@ impl FromLua for UserDataRefMut { let type_id = lua.get_userdata_type_id(idx)?; match type_id { Some(type_id) if type_id == TypeId::of::() => { - (*get_userdata::>(lua.state(), idx)).try_borrow_owned_mut() + (*get_userdata::>(lua.state(), idx)).try_borrow_owned_mut() } _ => Err(Error::UserDataTypeMismatch), } @@ -363,6 +354,124 @@ fn try_value_to_userdata(value: Value) -> Result { } } +pub(crate) enum ScopedUserDataVariant { + Ref(*const T), + RefMut(RefCell<*mut T>), + Boxed(RefCell<*mut T>), +} + +impl Drop for ScopedUserDataVariant { + #[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 UserDataStorage { + #[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; + 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> { + match self { + Self::Owned(data) => data.try_borrow_owned(), + Self::Scoped(_) => Err(Error::UserDataTypeMismatch), + } + } + + #[inline(always)] + pub(crate) fn try_borrow_mut(&self) -> Result> { + 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> { + match self { + Self::Owned(data) => data.try_borrow_owned_mut(), + Self::Scoped(_) => Err(Error::UserDataTypeMismatch), + } + } + + #[inline(always)] + pub(crate) fn into_inner(self) -> Result { + match self { + Self::Owned(data) => data.into_inner(), + Self::Scoped(_) => Err(Error::UserDataTypeMismatch), + } + } +} + +impl UserDataStorage { + #[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(&self, f: impl FnOnce(&T) -> R) -> Result { + 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(&self, f: impl FnOnce(&mut T) -> R) -> Result { + 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::*; diff --git a/src/userdata/registry.rs b/src/userdata/registry.rs index 79eb080b..ad4e3178 100644 --- a/src/userdata/registry.rs +++ b/src/userdata/registry.rs @@ -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 Result<()> + 'static>; +#[derive(Clone, Copy)] +pub(crate) enum UserDataTypeId { + Shared(TypeId), + Unique(usize), +} + /// Handle to registry for userdata methods and metamethods. -pub struct UserDataRegistry { +pub struct UserDataRegistry { // Fields pub(crate) fields: Vec<(String, StaticFieldCallback)>, pub(crate) field_getters: Vec<(String, Callback)>, @@ -41,11 +46,13 @@ pub struct UserDataRegistry { #[cfg(feature = "async")] pub(crate) async_meta_methods: Vec<(String, AsyncCallback)>, + pub(crate) type_id: UserDataTypeId, _type: PhantomData, } -impl UserDataRegistry { - pub(crate) const fn new() -> Self { +impl UserDataRegistry { + #[inline] + pub(crate) fn new(type_id: TypeId) -> Self { UserDataRegistry { fields: Vec::new(), field_getters: Vec::new(), @@ -57,11 +64,38 @@ impl UserDataRegistry { meta_methods: Vec::new(), #[cfg(feature = "async")] async_meta_methods: Vec::new(), + type_id: UserDataTypeId::Shared(type_id), _type: PhantomData, } } - fn box_method(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 { + match self.type_id { + UserDataTypeId::Shared(type_id) => Some(type_id), + UserDataTypeId::Unique(_) => None, + } + } + + fn box_method(&self, name: &str, method: M) -> Callback where M: Fn(&Lua, &T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, @@ -74,6 +108,7 @@ impl UserDataRegistry { }; } + 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 UserDataRegistry { // 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::() => { - let ud = try_self_arg!(borrow_userdata_ref::(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::>(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::>(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(name: &str, method: M) -> Callback + fn box_method_mut(&self, name: &str, method: M) -> Callback where M: FnMut(&Lua, &mut T, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, @@ -109,6 +161,7 @@ impl UserDataRegistry { } 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 UserDataRegistry { // 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::() => { - let mut ud = try_self_arg!(borrow_userdata_mut::(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::>(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::>(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(name: &str, method: M) -> AsyncCallback + fn box_async_method(&self, name: &str, method: M) -> AsyncCallback where + T: 'static, M: Fn(Lua, UserDataRef, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, @@ -171,8 +242,9 @@ impl UserDataRegistry { } #[cfg(feature = "async")] - fn box_async_method_mut(name: &str, method: M) -> AsyncCallback + fn box_async_method_mut(&self, name: &str, method: M) -> AsyncCallback where + T: 'static, M: Fn(Lua, UserDataRefMut, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + MaybeSend + 'static, @@ -209,7 +281,7 @@ impl UserDataRegistry { }) } - fn box_function(name: &str, function: F) -> Callback + fn box_function(&self, name: &str, function: F) -> Callback where F: Fn(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, @@ -222,7 +294,7 @@ impl UserDataRegistry { }) } - fn box_function_mut(name: &str, function: F) -> Callback + fn box_function_mut(&self, name: &str, function: F) -> Callback where F: FnMut(&Lua, A) -> Result + MaybeSend + 'static, A: FromLuaMulti, @@ -240,7 +312,7 @@ impl UserDataRegistry { } #[cfg(feature = "async")] - fn box_async_function(name: &str, function: F) -> AsyncCallback + fn box_async_function(&self, name: &str, function: F) -> AsyncCallback where F: Fn(Lua, A) -> FR + MaybeSend + 'static, A: FromLuaMulti, @@ -282,7 +354,7 @@ fn get_function_name(name: &str) -> StdString { format!("{}.{name}", short_type_name::()) } -impl UserDataFields for UserDataRegistry { +impl UserDataFields for UserDataRegistry { fn add_field(&mut self, name: impl ToString, value: V) where V: IntoLua + 'static, @@ -300,7 +372,7 @@ impl UserDataFields for UserDataRegistry { 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 UserDataFields for UserDataRegistry { 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 UserDataFields for UserDataRegistry { 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 UserDataFields for UserDataRegistry { 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 UserDataFields for UserDataRegistry { } } -impl UserDataMethods for UserDataRegistry { +impl UserDataMethods for UserDataRegistry { fn add_method(&mut self, name: impl ToString, method: M) where M: Fn(&Lua, &T, A) -> Result + MaybeSend + 'static, @@ -371,7 +443,7 @@ impl UserDataMethods for UserDataRegistry { 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 UserDataMethods for UserDataRegistry { 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(&mut self, name: impl ToString, method: M) where + T: 'static, M: Fn(Lua, UserDataRef, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + 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(&mut self, name: impl ToString, method: M) where + T: 'static, M: Fn(Lua, UserDataRefMut, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + 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 UserDataMethods for UserDataRegistry { 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 UserDataMethods for UserDataRegistry { 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 UserDataMethods for UserDataRegistry { 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 UserDataMethods for UserDataRegistry { 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 UserDataMethods for UserDataRegistry { 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(&mut self, name: impl ToString, method: M) where + T: 'static, M: Fn(Lua, UserDataRef, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + 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(&mut self, name: impl ToString, method: M) where + T: 'static, M: Fn(Lua, UserDataRefMut, A) -> MR + MaybeSend + 'static, A: FromLuaMulti, MR: Future> + 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 UserDataMethods for UserDataRegistry { 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 UserDataMethods for UserDataRegistry { 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 UserDataMethods for UserDataRegistry { 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> { - let ud = get_userdata::>(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> { - let ud = get_userdata::>(state, index); - (*ud).try_borrow_mut() -} - macro_rules! lua_userdata_impl { ($type:ty) => { impl UserData for $type { fn register(registry: &mut UserDataRegistry) { - let mut orig_registry = UserDataRegistry::new(); + let type_id = TypeId::of::(); + let mut orig_registry = UserDataRegistry::new(type_id); T::register(&mut orig_registry); // Copy all fields, methods, etc. from the original registry diff --git a/src/util/error.rs b/src/util/error.rs index 0cd4abef..05814171 100644 --- a/src/util/error.rs +++ b/src/util/error.rs @@ -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)?; diff --git a/src/util/mod.rs b/src/util/mod.rs index 93519c27..b0f886f0 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -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; diff --git a/src/util/userdata.rs b/src/util/userdata.rs index 59c00223..321bb903 100644 --- a/src/util/userdata.rs +++ b/src/util/userdata.rs @@ -83,46 +83,34 @@ pub(crate) unsafe fn get_internal_userdata( // Internally uses 3 stack spaces, does not call checkstack. #[inline] -pub(crate) unsafe fn push_userdata(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(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::()) as *mut T - })? - } else { - ffi::lua_newuserdata(state, std::mem::size_of::()) as *mut T - }; - #[cfg(feature = "luau")] - let ud = if protect { - protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::(state) })? + }) } else { - ffi::lua_newuserdata_t::(state) - }; - ptr::write(ud, t); - Ok(()) + Ok(ffi::lua_newuserdata(state, std::mem::size_of::()) as *mut T) + } } // Internally uses 3 stack spaces, does not call checkstack. -#[cfg(feature = "lua54")] #[inline] -pub(crate) unsafe fn push_userdata_uv( - 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::(), nuvalue) as *mut T - })? +pub(crate) unsafe fn push_userdata(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_ptr = if protect { + protect_lua!(state, 0, 1, |state| { ffi::lua_newuserdata_t::(state) })? } else { - ffi::lua_newuserdatauv(state, std::mem::size_of::(), nuvalue) as *mut T + ffi::lua_newuserdata_t::(state) }; - ptr::write(ud, t); - Ok(()) + ptr::write(ud_ptr, t); + Ok(ud_ptr) } #[inline] +#[track_caller] pub(crate) unsafe fn get_userdata(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"); diff --git a/tests/compile.rs b/tests/compile.rs index f237cad2..c8ee4511 100644 --- a/tests/compile.rs +++ b/tests/compile.rs @@ -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")] diff --git a/tests/compile/async_any_userdata_method.rs b/tests/compile/async_any_userdata_method.rs index a9f2f8d7..680eaebd 100644 --- a/tests/compile/async_any_userdata_method.rs +++ b/tests/compile/async_any_userdata_method.rs @@ -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::(|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(); } diff --git a/tests/compile/async_any_userdata_method.stderr b/tests/compile/async_any_userdata_method.stderr index a33b5b6e..970feeff 100644 --- a/tests/compile/async_any_userdata_method.stderr +++ b/tests/compile/async_any_userdata_method.stderr @@ -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[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, ()| 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:58 + --> 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 { + | ___________________________________-------------_^ + | | | | + | | | 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::(|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 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 { | ++++ diff --git a/tests/compile/async_nonstatic_userdata.rs b/tests/compile/async_nonstatic_userdata.rs index 8b5a7cff..d4a73eb9 100644 --- a/tests/compile/async_nonstatic_userdata.rs +++ b/tests/compile/async_nonstatic_userdata.rs @@ -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>(methods: &mut M) { methods.add_async_method("print", |_, data, ()| async move { println!("{}", data.0); Ok(()) diff --git a/tests/compile/async_nonstatic_userdata.stderr b/tests/compile/async_nonstatic_userdata.stderr index 316feb10..368e9f34 100644 --- a/tests/compile/async_nonstatic_userdata.stderr +++ b/tests/compile/async_nonstatic_userdata.stderr @@ -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>(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` diff --git a/tests/compile/async_userdata_method.rs b/tests/compile/async_userdata_method.rs deleted file mode 100644 index 7b07dc13..00000000 --- a/tests/compile/async_userdata_method.rs +++ /dev/null @@ -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() {} diff --git a/tests/compile/async_userdata_method.stderr b/tests/compile/async_userdata_method.stderr deleted file mode 100644 index a7f25a22..00000000 --- a/tests/compile/async_userdata_method.stderr +++ /dev/null @@ -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` diff --git a/tests/compile/function_borrow.rs b/tests/compile/function_borrow.rs index f64f3b8f..2c532361 100644 --- a/tests/compile/function_borrow.rs +++ b/tests/compile/function_borrow.rs @@ -6,7 +6,5 @@ fn main() { let test = Test(0); let lua = Lua::new(); - let _ = lua.create_function(|_, ()| -> Result { - Ok(test.0) - }); + let _ = lua.create_function(|_, ()| -> Result { Ok(test.0) }); } diff --git a/tests/compile/function_borrow.stderr b/tests/compile/function_borrow.stderr index 16a78a32..5bc66d65 100644 --- a/tests/compile/function_borrow.stderr +++ b/tests/compile/function_borrow.stderr @@ -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 { - | ^^^^^^^^^^^^^^^^^^^^^^ 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 { 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 { - | _____________^ -10 | | Ok(test.0) -11 | | }); - | |______^ + --> tests/compile/function_borrow.rs:9:13 + | +9 | let _ = lua.create_function(|_, ()| -> Result { 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 { - | ++++ + | +9 | let _ = lua.create_function(move |_, ()| -> Result { Ok(test.0) }); + | ++++ diff --git a/tests/compile/lua_norefunwindsafe.stderr b/tests/compile/lua_norefunwindsafe.stderr index a4410fec..ea2442bd 100644 --- a/tests/compile/lua_norefunwindsafe.stderr +++ b/tests/compile/lua_norefunwindsafe.stderr @@ -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` 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` 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`, 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`, 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` + --> $CARGO/lock_api-0.4.12/src/remutex.rs | - | pub struct Cell { - | ^^^^ -note: required because it appears within the type `mlua::state::raw::RawLua` - --> src/state/raw.rs + | pub struct ReentrantMutex { + | ^^^^^^^^^^^^^^ +note: required because it appears within the type `alloc::sync::ArcInner>` + --> $RUST/alloc/src/sync.rs + | + | struct ArcInner { + | ^^^^^^^^ +note: required because it appears within the type `PhantomData>>` + --> $RUST/core/src/marker.rs | - | pub struct RawLua { - | ^^^^^^ -note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex` - --> src/types/sync.rs + | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `Arc>` + --> $RUST/alloc/src/sync.rs | - | pub(crate) struct ReentrantMutex(T); - | ^^^^^^^^^^^^^^ - = note: required for `Rc>` to implement `RefUnwindSafe` + | pub struct Arc< + | ^^^ note: required because it appears within the type `Lua` --> src/state.rs | - | pub struct Lua(XRc>); + | 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 R + UnwindSafe, R>(f: F) -> Result { | ^^^^^^^^^^ required by this bound in `catch_unwind` -error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` 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` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` 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`, which is required by `{closure@$DIR/tests/compile/lua_norefunwindsafe.rs:7:18: 7:20}: UnwindSafe` - = note: required for `Rc>` 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`, 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` + --> $RUST/core/src/cell.rs | - | pub struct RawLua { - | ^^^^^^ -note: required because it appears within the type `mlua::types::sync::inner::ReentrantMutex` - --> src/types/sync.rs + | pub struct Cell { + | ^^^^ +note: required because it appears within the type `lock_api::remutex::RawReentrantMutex` + --> $CARGO/lock_api-0.4.12/src/remutex.rs + | + | pub struct RawReentrantMutex { + | ^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `lock_api::remutex::ReentrantMutex` + --> $CARGO/lock_api-0.4.12/src/remutex.rs + | + | pub struct ReentrantMutex { + | ^^^^^^^^^^^^^^ +note: required because it appears within the type `alloc::sync::ArcInner>` + --> $RUST/alloc/src/sync.rs | - | pub(crate) struct ReentrantMutex(T); - | ^^^^^^^^^^^^^^ - = note: required for `Rc>` to implement `RefUnwindSafe` + | struct ArcInner { + | ^^^^^^^^ +note: required because it appears within the type `PhantomData>>` + --> $RUST/core/src/marker.rs + | + | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `Arc>` + --> $RUST/alloc/src/sync.rs + | + | pub struct Arc< + | ^^^ note: required because it appears within the type `Lua` --> src/state.rs | - | pub struct Lua(XRc>); + | pub struct Lua { | ^^^ = note: required for `&Lua` to implement `UnwindSafe` note: required because it's used within this closure diff --git a/tests/compile/non_send.rs b/tests/compile/non_send.rs index cd21f445..6f6b1e99 100644 --- a/tests/compile/non_send.rs +++ b/tests/compile/non_send.rs @@ -8,10 +8,8 @@ fn main() -> Result<()> { let data = Rc::new(Cell::new(0)); - lua.create_function(move |_, ()| { - Ok(data.get()) - })? - .call::(())?; + lua.create_function(move |_, ()| Ok(data.get()))? + .call::(())?; Ok(()) } diff --git a/tests/compile/non_send.stderr b/tests/compile/non_send.stderr index 408b682f..c7e28da1 100644 --- a/tests/compile/non_send.stderr +++ b/tests/compile/non_send.stderr @@ -1,28 +1,25 @@ error[E0277]: `Rc>` 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>` cannot be sent between threads safely +11 | lua.create_function(move |_, ()| Ok(data.get()))? + | --------------- ------------^^^^^^^^^^^^^^^ + | | | + | | `Rc>` 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>` + = help: within `{closure@$DIR/tests/compile/non_send.rs:11:25: 11:37}`, the trait `Send` is not implemented for `Rc>`, 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> + | pub fn create_function(&self, func: F) -> Result | --------------- required by a bound in this associated function -... - | F: Fn(&'lua Lua, A) -> Result + MaybeSend + 'static, - | ^^^^^^^^^ required by this bound in `Lua::create_function` + | where + | F: Fn(&Lua, A) -> Result + MaybeSend + 'static, + | ^^^^^^^^^ required by this bound in `Lua::create_function` diff --git a/tests/compile/ref_nounwindsafe.stderr b/tests/compile/ref_nounwindsafe.stderr index 4c82de1b..39e70812 100644 --- a/tests/compile/ref_nounwindsafe.stderr +++ b/tests/compile/ref_nounwindsafe.stderr @@ -1,25 +1,25 @@ -error[E0277]: the type `UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +error[E0277]: the type `UnsafeCell` 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` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` 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>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell`, 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` - --> $RUST/core/src/cell.rs + = help: within `alloc::sync::ArcInner>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell`, 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` + --> $CARGO/lock_api-0.4.12/src/remutex.rs | - | pub struct Cell { - | ^^^^ -note: required because it appears within the type `rc::RcBox>` - --> $RUST/alloc/src/rc.rs + | pub struct ReentrantMutex { + | ^^^^^^^^^^^^^^ +note: required because it appears within the type `alloc::sync::ArcInner>` + --> $RUST/alloc/src/sync.rs | - | struct RcBox { - | ^^^^^ - = note: required for `NonNull>>` to implement `UnwindSafe` -note: required because it appears within the type `std::rc::Weak>` - --> $RUST/alloc/src/rc.rs + | struct ArcInner { + | ^^^^^^^^ + = note: required for `NonNull>>` to implement `UnwindSafe` +note: required because it appears within the type `std::sync::Weak>` + --> $RUST/alloc/src/sync.rs | | pub struct Weak< | ^^^^ @@ -49,95 +49,38 @@ note: required by a bound in `std::panic::catch_unwind` | pub fn catch_unwind R + UnwindSafe, R>(f: F) -> Result { | ^^^^^^^^^^ 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 +error[E0277]: the type `UnsafeCell` 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 + | ------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `UnsafeCell` 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>`, 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>` + = help: within `alloc::sync::ArcInner>`, the trait `RefUnwindSafe` is not implemented for `UnsafeCell`, 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` --> $RUST/core/src/cell.rs | | pub struct Cell { | ^^^^ -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` - --> src/types/sync.rs - | - | pub(crate) struct ReentrantMutex(T); - | ^^^^^^^^^^^^^^ -note: required because it appears within the type `rc::RcBox>` - --> $RUST/alloc/src/rc.rs - | - | struct RcBox { - | ^^^^^ - = note: required for `NonNull>>` to implement `UnwindSafe` -note: required because it appears within the type `std::rc::Weak>` - --> $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>); - | ^^^^^^^ -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 R + UnwindSafe, R>(f: F) -> Result { - | ^^^^^^^^^^ required by this bound in `catch_unwind` - -error[E0277]: the type `UnsafeCell` 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` 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`, which is required by `{closure@$DIR/tests/compile/ref_nounwindsafe.rs:8:18: 8:25}: UnwindSafe` - = note: required for `Rc>` 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` - --> src/types/sync.rs - | - | pub(crate) struct ReentrantMutex(T); - | ^^^^^^^^^^^^^^ -note: required because it appears within the type `rc::RcBox>` - --> $RUST/alloc/src/rc.rs - | - | struct RcBox { - | ^^^^^ - = note: required for `NonNull>>` to implement `UnwindSafe` -note: required because it appears within the type `std::rc::Weak>` - --> $RUST/alloc/src/rc.rs +note: required because it appears within the type `lock_api::remutex::RawReentrantMutex` + --> $CARGO/lock_api-0.4.12/src/remutex.rs + | + | pub struct RawReentrantMutex { + | ^^^^^^^^^^^^^^^^^ +note: required because it appears within the type `lock_api::remutex::ReentrantMutex` + --> $CARGO/lock_api-0.4.12/src/remutex.rs + | + | pub struct ReentrantMutex { + | ^^^^^^^^^^^^^^ +note: required because it appears within the type `alloc::sync::ArcInner>` + --> $RUST/alloc/src/sync.rs + | + | struct ArcInner { + | ^^^^^^^^ + = note: required for `NonNull>>` to implement `UnwindSafe` +note: required because it appears within the type `std::sync::Weak>` + --> $RUST/alloc/src/sync.rs | | pub struct Weak< | ^^^^ diff --git a/tests/compile/scope_callback_capture.rs b/tests/compile/scope_callback_capture.rs index 8f0d0b07..9f8ec53c 100644 --- a/tests/compile/scope_callback_capture.rs +++ b/tests/compile/scope_callback_capture.rs @@ -4,14 +4,10 @@ fn main() { let lua = Lua::new(); lua.scope(|scope| { let mut inner: Option = 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(()) }); diff --git a/tests/compile/scope_callback_capture.stderr b/tests/compile/scope_callback_capture.stderr index 2b641480..a6993916 100644 --- a/tests/compile/scope_callback_capture.stderr +++ b/tests/compile/scope_callback_capture.stderr @@ -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
= 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| { + | ++++ diff --git a/tests/compile/scope_callback_inner.rs b/tests/compile/scope_callback_inner.rs deleted file mode 100644 index b7958b47..00000000 --- a/tests/compile/scope_callback_inner.rs +++ /dev/null @@ -1,15 +0,0 @@ -use mlua::{Lua, Table}; - -fn main() { - let lua = Lua::new(); - lua.scope(|scope| { - let mut inner: Option
= None; - let f = scope - .create_function_mut(|_, t: Table| { - inner = Some(t); - Ok(()) - })?; - f.call::<()>(lua.create_table()?)?; - Ok(()) - }); -} diff --git a/tests/compile/scope_callback_inner.stderr b/tests/compile/scope_callback_inner.stderr deleted file mode 100644 index 5fb9a570..00000000 --- a/tests/compile/scope_callback_inner.stderr +++ /dev/null @@ -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` diff --git a/tests/compile/scope_callback_outer.rs b/tests/compile/scope_callback_outer.rs deleted file mode 100644 index 8aa09868..00000000 --- a/tests/compile/scope_callback_outer.rs +++ /dev/null @@ -1,15 +0,0 @@ -use mlua::{Lua, Table}; - -fn main() { - let lua = Lua::new(); - let mut outer: Option
= None; - lua.scope(|scope| { - let f = scope - .create_function_mut(|_, t: Table| { - outer = Some(t); - Ok(()) - })?; - f.call::<()>(lua.create_table()?)?; - Ok(()) - }); -} diff --git a/tests/compile/scope_callback_outer.stderr b/tests/compile/scope_callback_outer.stderr deleted file mode 100644 index dfafdaee..00000000 --- a/tests/compile/scope_callback_outer.stderr +++ /dev/null @@ -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` diff --git a/tests/compile/scope_invariance.rs b/tests/compile/scope_invariance.rs index abf44bb9..0414efc8 100644 --- a/tests/compile/scope_invariance.rs +++ b/tests/compile/scope_invariance.rs @@ -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::<()>(()) diff --git a/tests/compile/scope_invariance.stderr b/tests/compile/scope_invariance.stderr index 40ed6850..91158aa6 100644 --- a/tests/compile/scope_invariance.stderr +++ b/tests/compile/scope_invariance.stderr @@ -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 |_, ()| { + | ++++ diff --git a/tests/compile/scope_mutable_aliasing.rs b/tests/compile/scope_mutable_aliasing.rs index 4e1dcf9e..4745296b 100644 --- a/tests/compile/scope_mutable_aliasing.rs +++ b/tests/compile/scope_mutable_aliasing.rs @@ -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(()) }); } diff --git a/tests/compile/scope_mutable_aliasing.stderr b/tests/compile/scope_mutable_aliasing.stderr index adbb63b5..362cf91d 100644 --- a/tests/compile/scope_mutable_aliasing.stderr +++ b/tests/compile/scope_mutable_aliasing.stderr @@ -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 diff --git a/tests/compile/scope_userdata_borrow.rs b/tests/compile/scope_userdata_borrow.rs index 24652344..53b1b813 100644 --- a/tests/compile/scope_userdata_borrow.rs +++ b/tests/compile/scope_userdata_borrow.rs @@ -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(()) }); diff --git a/tests/compile/scope_userdata_borrow.stderr b/tests/compile/scope_userdata_borrow.stderr index 87abf7b8..043a99b1 100644 --- a/tests/compile/scope_userdata_borrow.stderr +++ b/tests/compile/scope_userdata_borrow.stderr @@ -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 diff --git a/tests/compile/userdata_borrow.rs b/tests/compile/userdata_borrow.rs deleted file mode 100644 index d29e5275..00000000 --- a/tests/compile/userdata_borrow.rs +++ /dev/null @@ -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::
("touter")?; - touter.set("userdata", lua.create_userdata(MyUserData)?)?; - let userdata = touter.get::("userdata")?; - _userdata_ref = userdata.borrow::(); - //~^ error: `userdata` does not live long enough - } - Ok(()) -} diff --git a/tests/compile/userdata_borrow.stderr b/tests/compile/userdata_borrow.stderr deleted file mode 100644 index 7ac96703..00000000 --- a/tests/compile/userdata_borrow.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0597]: `userdata` does not live long enough - --> $DIR/userdata_borrow.rs:15:25 - | -15 | _userdata_ref = userdata.borrow::(); - | ^^^^^^^^ 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, mlua::error::Error>` - | - = note: values in a scope are dropped in the opposite order they are defined diff --git a/tests/scope.rs.1 b/tests/scope.rs similarity index 55% rename from tests/scope.rs.1 rename to tests/scope.rs index b04b7b50..edc06db6 100644 --- a/tests/scope.rs.1 +++ b/tests/scope.rs @@ -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::("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::("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); - 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) { + 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); - 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) { + 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) { + 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::("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::("i")? + this.0)?; Ok(()) }); } @@ -184,9 +179,34 @@ fn test_scope_userdata_functions() -> Result<()> { ) .eval::()?; - 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::("i")?, 3); + + Ok(()) +} + +#[test] +fn test_scope_userdata_values() -> Result<()> { + struct MyUserData<'a>(&'a i64); + + impl UserData for MyUserData<'_> { + fn register(registry: &mut UserDataRegistry) { + 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::("get", &ud)?, 42); + ud.set_user_value("user_value")?; + assert_eq!(ud.user_value::()?, "user_value"); + Ok(()) + })?; Ok(()) } @@ -196,8 +216,8 @@ fn test_scope_userdata_mismatch() -> Result<()> { struct MyUserData<'a>(&'a Cell); 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) { + 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<()>); - - 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::() { - 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, #[allow(unused)] Arc<()>); + struct MyUserData<'a>(&'a Cell, #[allow(unused)] Rc<()>); - 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) { + 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::() { + let ud = lua.globals().get::("ud")?; + match ud.borrow_scoped::(|_| 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); impl UserData for MyUserData { - fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { + fn add_methods>(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>(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(()) } diff --git a/tests/serde.rs b/tests/serde.rs index f4bd67a9..fb50c04c 100644 --- a/tests/serde.rs +++ b/tests/serde.rs @@ -71,50 +71,6 @@ fn test_serialize() -> Result<(), Box> { 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::()?; -// 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> { let lua = Lua::new(); diff --git a/tests/userdata.rs b/tests/userdata.rs index e305ae38..b78e0171 100644 --- a/tests/userdata.rs +++ b/tests/userdata.rs @@ -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), }