Store nonstatic UserData pointer in self userdata (instead of metatable)
This commit is contained in:
parent
0625991a48
commit
b5f1325f2f
59
src/scope.rs
59
src/scope.rs
|
@ -2,8 +2,7 @@ use std::any::Any;
|
||||||
use std::cell::{Cell, Ref, RefCell, RefMut};
|
use std::cell::{Cell, Ref, RefCell, RefMut};
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::os::raw::{c_int, c_void};
|
use std::os::raw::c_int;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
#[cfg(feature = "serialize")]
|
#[cfg(feature = "serialize")]
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
@ -17,7 +16,7 @@ use crate::userdata::{
|
||||||
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataWrapped,
|
AnyUserData, MetaMethod, UserData, UserDataFields, UserDataMethods, UserDataWrapped,
|
||||||
};
|
};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
assert_stack, init_userdata_metatable, push_userdata, take_userdata, StackGuard,
|
assert_stack, get_userdata, init_userdata_metatable, push_userdata, take_userdata, StackGuard,
|
||||||
};
|
};
|
||||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
use crate::value::{FromLua, FromLuaMulti, MultiValue, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
|
@ -187,9 +186,12 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
let newtable = self.lua.create_table()?;
|
let newtable = self.lua.create_table()?;
|
||||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||||
let state = ud.lua.state;
|
let state = ud.lua.state;
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
assert_stack(state, 2);
|
assert_stack(state, 2);
|
||||||
ud.lua.push_ref(&ud);
|
ud.lua.push_ref(&ud);
|
||||||
|
|
||||||
|
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||||
|
|
||||||
// Clear uservalue
|
// Clear uservalue
|
||||||
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52"))]
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(state);
|
||||||
|
@ -197,8 +199,6 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
ud.lua.push_ref(&newtable.0);
|
ud.lua.push_ref(&newtable.0);
|
||||||
ffi::lua_setuservalue(state, -2);
|
ffi::lua_setuservalue(state, -2);
|
||||||
|
|
||||||
// We know the destructor has not run yet because we hold a reference to the
|
|
||||||
// userdata.
|
|
||||||
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
vec![Box::new(take_userdata::<UserDataCell<T>>(state))]
|
||||||
});
|
});
|
||||||
self.destructors
|
self.destructors
|
||||||
|
@ -236,7 +236,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
where
|
where
|
||||||
T: 'scope + UserData,
|
T: 'scope + UserData,
|
||||||
{
|
{
|
||||||
let data = Rc::new(RefCell::new(UserDataWrapped::new(data)));
|
let data = UserDataCell::new(UserDataWrapped::new(data));
|
||||||
|
|
||||||
// 'callback outliving 'scope is a lie to make the types work out, required due to the
|
// '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
|
// inability to work with the more correct callback type that is universally quantified over
|
||||||
|
@ -245,7 +245,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
// parameters.
|
// parameters.
|
||||||
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
fn wrap_method<'scope, 'lua, 'callback: 'scope, T: 'scope>(
|
||||||
scope: &Scope<'lua, 'scope>,
|
scope: &Scope<'lua, 'scope>,
|
||||||
data: Rc<UserDataCell<T>>,
|
data: *mut UserDataCell<T>,
|
||||||
method: NonStaticMethod<'callback, T>,
|
method: NonStaticMethod<'callback, T>,
|
||||||
) -> Result<Function<'lua>> {
|
) -> Result<Function<'lua>> {
|
||||||
// On methods that actually receive the userdata, we fake a type check on the passed in
|
// On methods that actually receive the userdata, we fake a type check on the passed in
|
||||||
|
@ -255,22 +255,15 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
// with a type mismatch, but here without this check would proceed as though you had
|
// 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
|
// called the method on the original value (since we otherwise completely ignore the
|
||||||
// first argument).
|
// first argument).
|
||||||
let check_data = data.clone();
|
let check_data = data;
|
||||||
let check_ud_type = move |lua: &'callback Lua, value| {
|
let check_ud_type = move |lua: &'callback Lua, value| {
|
||||||
if let Some(Value::UserData(ud)) = value {
|
if let Some(Value::UserData(ud)) = value {
|
||||||
unsafe {
|
unsafe {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 3);
|
assert_stack(lua.state, 3);
|
||||||
lua.push_ref(&ud.0);
|
lua.push_userdata_ref(&ud.0)?;
|
||||||
if ffi::lua_getmetatable(lua.state, -1) == 0 {
|
if get_userdata(lua.state, -1) == check_data {
|
||||||
return Err(Error::UserDataTypeMismatch);
|
return Ok(());
|
||||||
}
|
|
||||||
ffi::safe::lua_pushstring(lua.state, "__mlua_ptr")?;
|
|
||||||
if ffi::lua_rawget(lua.state, -2) == ffi::LUA_TLIGHTUSERDATA {
|
|
||||||
let ud_ptr = ffi::lua_touserdata(lua.state, -1);
|
|
||||||
if ud_ptr == check_data.as_ptr() as *mut c_void {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -281,7 +274,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
NonStaticMethod::Method(method) => {
|
NonStaticMethod::Method(method) => {
|
||||||
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
let f = Box::new(move |lua, mut args: MultiValue<'callback>| {
|
||||||
check_ud_type(lua, args.pop_front())?;
|
check_ud_type(lua, args.pop_front())?;
|
||||||
let data = data
|
let data = unsafe { &*data }
|
||||||
.try_borrow()
|
.try_borrow()
|
||||||
.map(|cell| Ref::map(cell, AsRef::as_ref))
|
.map(|cell| Ref::map(cell, AsRef::as_ref))
|
||||||
.map_err(|_| Error::UserDataBorrowError)?;
|
.map_err(|_| Error::UserDataBorrowError)?;
|
||||||
|
@ -296,7 +289,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
let mut method = method
|
let mut method = method
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map_err(|_| Error::RecursiveMutCallback)?;
|
.map_err(|_| Error::RecursiveMutCallback)?;
|
||||||
let mut data = data
|
let mut data = unsafe { &mut *data }
|
||||||
.try_borrow_mut()
|
.try_borrow_mut()
|
||||||
.map(|cell| RefMut::map(cell, AsMut::as_mut))
|
.map(|cell| RefMut::map(cell, AsMut::as_mut))
|
||||||
.map_err(|_| Error::UserDataBorrowMutError)?;
|
.map_err(|_| Error::UserDataBorrowMutError)?;
|
||||||
|
@ -329,18 +322,15 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
let _sg = StackGuard::new(lua.state);
|
let _sg = StackGuard::new(lua.state);
|
||||||
assert_stack(lua.state, 13);
|
assert_stack(lua.state, 13);
|
||||||
|
|
||||||
// We need to wrap dummy userdata because their memory can be accessed by serializer
|
push_userdata(lua.state, data)?;
|
||||||
push_userdata(lua.state, UserDataCell::new(UserDataWrapped::new(())))?;
|
let data = get_userdata::<UserDataCell<T>>(lua.state, -1);
|
||||||
|
|
||||||
// Prepare metatable, add meta methods first and then meta fields
|
// Prepare metatable, add meta methods first and then meta fields
|
||||||
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
|
let meta_methods_nrec = ud_methods.meta_methods.len() + ud_fields.meta_fields.len() + 1;
|
||||||
ffi::safe::lua_createtable(lua.state, 0, meta_methods_nrec as c_int)?;
|
ffi::safe::lua_createtable(lua.state, 0, meta_methods_nrec as c_int)?;
|
||||||
|
|
||||||
ffi::lua_pushlightuserdata(lua.state, data.as_ptr() as *mut c_void);
|
|
||||||
ffi::safe::lua_rawsetfield(lua.state, -2, "__mlua_ptr")?;
|
|
||||||
|
|
||||||
for (k, m) in ud_methods.meta_methods {
|
for (k, m) in ud_methods.meta_methods {
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
lua.push_value(Value::Function(wrap_method(self, data, m)?))?;
|
||||||
ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?;
|
ffi::safe::lua_rawsetfield(lua.state, -2, k.validate()?.name())?;
|
||||||
}
|
}
|
||||||
for (k, f) in ud_fields.meta_fields {
|
for (k, f) in ud_fields.meta_fields {
|
||||||
|
@ -354,7 +344,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
if field_getters_nrec > 0 {
|
if field_getters_nrec > 0 {
|
||||||
ffi::safe::lua_createtable(lua.state, 0, field_getters_nrec as c_int)?;
|
ffi::safe::lua_createtable(lua.state, 0, field_getters_nrec as c_int)?;
|
||||||
for (k, m) in ud_fields.field_getters {
|
for (k, m) in ud_fields.field_getters {
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
lua.push_value(Value::Function(wrap_method(self, data, m)?))?;
|
||||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||||
}
|
}
|
||||||
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
|
field_getters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||||
|
@ -365,7 +355,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
if field_setters_nrec > 0 {
|
if field_setters_nrec > 0 {
|
||||||
ffi::safe::lua_createtable(lua.state, 0, field_setters_nrec as c_int)?;
|
ffi::safe::lua_createtable(lua.state, 0, field_setters_nrec as c_int)?;
|
||||||
for (k, m) in ud_fields.field_setters {
|
for (k, m) in ud_fields.field_setters {
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
lua.push_value(Value::Function(wrap_method(self, data, m)?))?;
|
||||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||||
}
|
}
|
||||||
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
|
field_setters_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||||
|
@ -377,7 +367,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
// Create table used for methods lookup
|
// Create table used for methods lookup
|
||||||
ffi::safe::lua_createtable(lua.state, 0, methods_nrec as c_int)?;
|
ffi::safe::lua_createtable(lua.state, 0, methods_nrec as c_int)?;
|
||||||
for (k, m) in ud_methods.methods {
|
for (k, m) in ud_methods.methods {
|
||||||
lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?))?;
|
lua.push_value(Value::Function(wrap_method(self, data, m)?))?;
|
||||||
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
ffi::safe::lua_rawsetfield(lua.state, -2, &k)?;
|
||||||
}
|
}
|
||||||
methods_index = Some(ffi::lua_absindex(lua.state, -1));
|
methods_index = Some(ffi::lua_absindex(lua.state, -1));
|
||||||
|
@ -404,11 +394,13 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
#[cfg(any(feature = "lua51", feature = "luajit"))]
|
||||||
let newtable = lua.create_table()?;
|
let newtable = lua.create_table()?;
|
||||||
let destructor: DestructorCallback = Box::new(move |ud| {
|
let destructor: DestructorCallback = Box::new(move |ud| {
|
||||||
// We know the destructor has not run yet because we hold a reference to the userdata.
|
|
||||||
let state = ud.lua.state;
|
let state = ud.lua.state;
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
assert_stack(state, 2);
|
assert_stack(state, 2);
|
||||||
ud.lua.push_ref(&ud);
|
ud.lua.push_ref(&ud);
|
||||||
|
|
||||||
|
// We know the destructor has not run yet because we hold a reference to the userdata.
|
||||||
|
|
||||||
// Deregister metatable
|
// Deregister metatable
|
||||||
ffi::lua_getmetatable(state, -1);
|
ffi::lua_getmetatable(state, -1);
|
||||||
let mt_id = ffi::lua_topointer(state, -1);
|
let mt_id = ffi::lua_topointer(state, -1);
|
||||||
|
@ -422,7 +414,9 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
ud.lua.push_ref(&newtable.0);
|
ud.lua.push_ref(&newtable.0);
|
||||||
ffi::lua_setuservalue(state, -2);
|
ffi::lua_setuservalue(state, -2);
|
||||||
|
|
||||||
vec![Box::new(take_userdata::<UserDataCell<()>>(state))]
|
// We cannot put `T` into the vec because `T` does not implement `Any`
|
||||||
|
drop(take_userdata::<UserDataCell<T>>(state));
|
||||||
|
vec![]
|
||||||
});
|
});
|
||||||
self.destructors
|
self.destructors
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
|
@ -446,6 +440,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
|
|
||||||
let destructor: DestructorCallback = Box::new(|f| {
|
let destructor: DestructorCallback = Box::new(|f| {
|
||||||
let state = f.lua.state;
|
let state = f.lua.state;
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
assert_stack(state, 3);
|
assert_stack(state, 3);
|
||||||
f.lua.push_ref(&f);
|
f.lua.push_ref(&f);
|
||||||
|
|
||||||
|
@ -461,7 +456,6 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
ffi::lua_pushnil(state);
|
ffi::lua_pushnil(state);
|
||||||
ffi::lua_setupvalue(state, -2, 3);
|
ffi::lua_setupvalue(state, -2, 3);
|
||||||
|
|
||||||
ffi::lua_pop(state, 1);
|
|
||||||
vec![Box::new(ud1), Box::new(ud2)]
|
vec![Box::new(ud1), Box::new(ud2)]
|
||||||
});
|
});
|
||||||
self.destructors
|
self.destructors
|
||||||
|
@ -484,6 +478,7 @@ impl<'lua, 'scope> Scope<'lua, 'scope> {
|
||||||
let poll_str = self.lua.create_string("poll")?;
|
let poll_str = self.lua.create_string("poll")?;
|
||||||
let destructor: DestructorCallback = Box::new(move |f| {
|
let destructor: DestructorCallback = Box::new(move |f| {
|
||||||
let state = f.lua.state;
|
let state = f.lua.state;
|
||||||
|
let _sg = StackGuard::new(state);
|
||||||
assert_stack(state, 4);
|
assert_stack(state, 4);
|
||||||
f.lua.push_ref(&f);
|
f.lua.push_ref(&f);
|
||||||
|
|
||||||
|
|
|
@ -302,7 +302,7 @@ fn scope_userdata_drop() -> Result<()> {
|
||||||
fn scope_nonstatic_userdata_drop() -> Result<()> {
|
fn scope_nonstatic_userdata_drop() -> Result<()> {
|
||||||
let lua = Lua::new();
|
let lua = Lua::new();
|
||||||
|
|
||||||
struct MyUserData<'a>(&'a Cell<i64>);
|
struct MyUserData<'a>(&'a Cell<i64>, Arc<()>);
|
||||||
|
|
||||||
impl<'a> UserData for MyUserData<'a> {
|
impl<'a> UserData for MyUserData<'a> {
|
||||||
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
@ -320,11 +320,11 @@ fn scope_nonstatic_userdata_drop() -> Result<()> {
|
||||||
let i = Cell::new(1);
|
let i = Cell::new(1);
|
||||||
let arc = Arc::new(());
|
let arc = Arc::new(());
|
||||||
lua.scope(|scope| {
|
lua.scope(|scope| {
|
||||||
let ud = scope.create_nonstatic_userdata(MyUserData(&i))?;
|
let ud = scope.create_nonstatic_userdata(MyUserData(&i, arc.clone()))?;
|
||||||
ud.set_user_value(MyUserDataArc(arc.clone()))?;
|
ud.set_user_value(MyUserDataArc(arc.clone()))?;
|
||||||
lua.globals().set("ud", ud)?;
|
lua.globals().set("ud", ud)?;
|
||||||
lua.load("ud:inc()").exec()?;
|
lua.load("ud:inc()").exec()?;
|
||||||
assert_eq!(Arc::strong_count(&arc), 2);
|
assert_eq!(Arc::strong_count(&arc), 3);
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue