From eebb93a9a6d9ba96cca071c2d7d34f20d7c47b80 Mon Sep 17 00:00:00 2001 From: SeanOMik Date: Mon, 29 Jan 2024 11:03:49 -0500 Subject: [PATCH] Store userdata pointers as RefCells to avoid multiple mutable borrows at once --- src/main.rs | 8 +++----- src/state.rs | 6 +++--- src/userdata.rs | 36 +++++++++++++++++++----------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index d6ad586..2f91304 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use std::{marker::PhantomData, sync::Arc}; +use std::{cell::Ref, marker::PhantomData, sync::Arc}; use mlua_sys as lua; @@ -392,7 +392,6 @@ pub struct Vec2 { } impl Userdata for Vec2 { - fn build<'a>(builder: &mut UserdataBuilder<'a, Vec2>) -> crate::Result<()> { builder .field_getter("x", |_, this| Ok(this.x)) @@ -406,7 +405,7 @@ impl Userdata for Vec2 { }) // method test - .method("add", |lua, lhs: &Vec2, (rhs,): (&Vec3,)| { + .method("add", |lua, lhs: &Vec2, (rhs,): (Ref,)| { let lx = lhs.x; let ly = lhs.y; @@ -416,7 +415,7 @@ impl Userdata for Vec2 { lua.create_userdata(Vec2 { x: lx + rx, y: ly + ry, }) }) - .meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (&Vec2,)| { + .meta_method(MetaMethod::Add, |lua, lhs: &Vec2, (rhs,): (Ref,)| { let lx = lhs.x; let ly = lhs.y; @@ -450,7 +449,6 @@ impl<'a, T: Userdata> AsLua<'a> for UserdataProxy { impl Userdata for UserdataProxy { fn build<'a>(builder: &mut UserdataBuilder<'a, Self>) -> crate::Result<()> { - let mut other = UserdataBuilder::::new(); T::build(&mut other)?; diff --git a/src/state.rs b/src/state.rs index 94431ec..a05aff4 100755 --- a/src/state.rs +++ b/src/state.rs @@ -1,5 +1,5 @@ use core::ffi; -use std::{alloc::{self, Layout}, any::TypeId, collections::HashMap, ffi::{CStr, CString}, mem, ptr::{self, NonNull}, str::Utf8Error, sync::Arc}; +use std::{alloc::{self, Layout}, any::TypeId, cell::RefCell, collections::HashMap, ffi::{CStr, CString}, mem, ptr::{self, NonNull}, str::Utf8Error, sync::Arc}; use mlua_sys as lua; @@ -402,8 +402,8 @@ impl State { let _g = StackGuard::new(self); let s = self.state_ptr(); - let ptr = lua::lua_newuserdata(s, mem::size_of::()).cast::(); - ptr::write(ptr, data); + let ptr = lua::lua_newuserdata(s, mem::size_of::>()).cast::>(); + ptr::write(ptr, RefCell::new(data)); // get the current metatable, or create a new one and push it to the stack. let udmts = &mut self.get_extra_space().userdata_metatables; diff --git a/src/userdata.rs b/src/userdata.rs index 5f85b2e..340e5f2 100755 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, ffi::CStr, marker::PhantomData}; +use std::{borrow::Borrow, cell::{Ref, RefCell, RefMut}, collections::HashMap, ffi::CStr, marker::PhantomData, ops::{Deref, DerefMut}}; use crate::{ensure_type, AsLua, FromLua, FromLuaStack, FromLuaVec, LuaRef, PushToLuaStack, StackGuard, State, Value, ValueVec}; @@ -133,7 +133,7 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { let val = val.pop_front().unwrap(); let this = val.as_userdata().unwrap(); // if this panics, its a bug let this = this.as_ref::()?; - f(lua, this).and_then(|r| r.as_lua(lua)) + f(lua, &*this).and_then(|r| r.as_lua(lua)) }; self.field_getters.insert(name.to_string(), Box::new(wrap)); @@ -149,12 +149,12 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { let wrap = move |lua: &'a State, mut val: ValueVec<'a>| { let lua_val = val.pop_front().unwrap(); let this = lua_val.as_userdata().unwrap(); // if this panics, its a bug - let this = this.as_mut::()?; + let mut this = this.as_mut::()?; let lua_val = val.pop_front().unwrap(); let v_arg = V::from_lua(lua, lua_val)?; - f(lua, this, v_arg).as_lua(lua) + f(lua, this.deref_mut(), v_arg).as_lua(lua) }; self.field_setters.insert(name.to_string(), Box::new(wrap)); @@ -211,7 +211,7 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { &this_name, &fn_name )?; - f(lua, this, args).and_then(|r| r.as_lua(lua)) + f(lua, &*this, args).and_then(|r| r.as_lua(lua)) }; self.functions.insert(name.to_string(), Box::new(wrap)); @@ -238,7 +238,7 @@ impl<'a, T: Userdata> UserdataBuilder<'a, T> { &this_name, &fn_name )?; - f(lua, this, args).and_then(|r| r.as_lua(lua)) + f(lua, &*this, args).and_then(|r| r.as_lua(lua)) }; self.meta_methods.insert(name.as_ref().to_string(), Box::new(wrap)); @@ -268,7 +268,7 @@ impl<'a> AnyUserdata<'a> { } /// Returns a borrow to the userdata. - pub fn as_ref(&self) -> crate::Result<&'a T> { + pub fn as_ref(&self) -> crate::Result> { unsafe { self.state.ensure_stack(3)?; let _g = StackGuard::new(self.state); @@ -285,7 +285,8 @@ impl<'a> AnyUserdata<'a> { if lua::lua_rawequal(s, -2, -1) == 1 { let cptr = lua::lua_touserdata(s, -3); - Ok(cptr.cast::().as_ref().unwrap()) // TODO: Ensure this userdata matches the type of T + let cell = &*cptr.cast::>(); + Ok(cell.borrow()) } else { return Err(crate::Error::UserdataMismatch); } @@ -293,7 +294,7 @@ impl<'a> AnyUserdata<'a> { } /// Returns a mutable reference to the userdata. - pub fn as_mut(&self) -> crate::Result<&'a mut T> { + pub fn as_mut(&self) -> crate::Result> { unsafe { self.state.ensure_stack(3)?; let _g = StackGuard::new(self.state); @@ -310,14 +311,15 @@ impl<'a> AnyUserdata<'a> { if lua::lua_rawequal(s, -2, -1) == 1 { let cptr = lua::lua_touserdata(s, -3); - Ok(cptr.cast::().as_mut().unwrap()) + let cell = &*cptr.cast::>(); + Ok(cell.borrow_mut()) } else { Err(crate::Error::UserdataMismatch) } } } - /// Returns a mutable pointer to the userdata **WITHOUT verifying the type of it**. + /// Returns a mutable pointer of the [`RefCell`] of userdata **WITHOUT verifying the type of it**. /// /// # Safety /// * You must be certain that the type `T` is the same type that this userdata has a handle to. @@ -325,7 +327,7 @@ impl<'a> AnyUserdata<'a> { /// /// If there is a possibility that these types do not match, use [`AnyUserdata::as_ptr`] /// which does verify the types. - pub unsafe fn as_ptr_unchecked(&self) -> crate::Result<*mut T> { + pub unsafe fn as_ptr_unchecked(&self) -> crate::Result<*mut RefCell> { self.state.ensure_stack(1)?; let _g = StackGuard::new(self.state); let s = self.state.state_ptr(); @@ -336,11 +338,11 @@ impl<'a> AnyUserdata<'a> { Ok(cptr.cast()) } - /// Returns a mutable pointer to the userdata. + /// Returns a mutable pointer of the [`RefCell`] storing the userdata. /// /// This function ensures that the type of the userdata this struct has a handle to is the /// same as `T`. If it isn't, a `UserdataMismatch` error will be returned. - pub unsafe fn as_ptr(&self) -> crate::Result<*mut T> { + pub unsafe fn as_ptr(&self) -> crate::Result<*mut RefCell> { let _g = StackGuard::new(self.state); let s = self.state.state_ptr(); @@ -355,7 +357,7 @@ impl<'a> AnyUserdata<'a> { if lua::lua_rawequal(s, -2, -1) == 1 { let cptr = lua::lua_touserdata(s, -3); - Ok(cptr.cast::()) + Ok(cptr.cast()) } else { Err(crate::Error::UserdataMismatch) } @@ -418,14 +420,14 @@ impl<'a> FromLua<'a> for AnyUserdata<'a> { } } -impl<'a, T: Userdata + 'static> FromLua<'a> for &'a T { +impl<'a, T: Userdata + 'static> FromLua<'a> for Ref<'a, T> { fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result { let ud = val.into_userdata()?; ud.as_ref::() } } -impl<'a, T: Userdata + 'static> FromLua<'a> for &'a mut T { +impl<'a, T: Userdata + 'static> FromLua<'a> for RefMut<'a, T> { fn from_lua(_lua: &'a State, val: Value<'a>) -> crate::Result { let ud = val.into_userdata()?; ud.as_mut::()