Skip to content

Commit 2fefaaf

Browse files
committed
Update "AnyUserData::take" to work on ref thread without need to push into stack.
1 parent 76a8f8c commit 2fefaaf

File tree

4 files changed

+31
-40
lines changed

4 files changed

+31
-40
lines changed

src/scope.rs

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,7 @@ use crate::state::{Lua, LuaGuard, RawLua};
88
use crate::traits::{FromLuaMulti, IntoLuaMulti};
99
use crate::types::{Callback, CallbackUpvalue, ScopedCallback, ValueRef};
1010
use crate::userdata::{AnyUserData, UserData, UserDataRegistry, UserDataStorage};
11-
use crate::util::{
12-
self, assert_stack, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard,
13-
};
11+
use crate::util::{self, check_stack, get_metatable_ptr, get_userdata, take_userdata, StackGuard};
1412

1513
/// Constructed by the [`Lua::scope`] method, allows temporarily creating Lua userdata and
1614
/// callbacks that are not required to be `Send` or `'static`.
@@ -284,22 +282,18 @@ impl<'scope, 'env: 'scope> Scope<'scope, 'env> {
284282
/// Shortens the lifetime of the userdata to the lifetime of the scope.
285283
fn seal_userdata<T: 'env>(&self, ud: &AnyUserData) {
286284
let destructor: DestructorCallback = Box::new(|rawlua, vref| unsafe {
287-
let state = rawlua.state();
288-
let _sg = StackGuard::new(state);
289-
assert_stack(state, 2);
290-
291285
// Ensure that userdata is not destructed
292-
match rawlua.push_userdata_ref(&vref) {
286+
match rawlua.get_userdata_ref_type_id(&vref) {
293287
Ok(Some(_)) => {}
294288
Ok(None) => {
295289
// Deregister metatable
296-
let mt_ptr = get_metatable_ptr(state, -1);
290+
let mt_ptr = get_metatable_ptr(rawlua.ref_thread(), vref.index);
297291
rawlua.deregister_userdata_metatable(mt_ptr);
298292
}
299293
Err(_) => return vec![],
300294
}
301295

302-
let data = take_userdata::<UserDataStorage<T>>(state);
296+
let data = take_userdata::<UserDataStorage<T>>(rawlua.ref_thread(), vref.index);
303297
vec![Box::new(move || drop(data))]
304298
});
305299
self.destructors.0.borrow_mut().push((ud.0.clone(), destructor));

src/userdata.rs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -683,22 +683,16 @@ impl AnyUserData {
683683
/// Keeps associated user values unchanged (they will be collected by Lua's GC).
684684
pub fn take<T: 'static>(&self) -> Result<T> {
685685
let lua = self.0.lua.lock();
686-
let state = lua.state();
687-
unsafe {
688-
let _sg = StackGuard::new(state);
689-
check_stack(state, 2)?;
690-
691-
let type_id = lua.push_userdata_ref(&self.0)?;
692-
match type_id {
693-
Some(type_id) if type_id == TypeId::of::<T>() => {
694-
if (*get_userdata::<UserDataStorage<T>>(state, -1)).has_exclusive_access() {
695-
take_userdata::<UserDataStorage<T>>(state).into_inner()
696-
} else {
697-
Err(Error::UserDataBorrowMutError)
698-
}
686+
match lua.get_userdata_ref_type_id(&self.0)? {
687+
Some(type_id) if type_id == TypeId::of::<T>() => unsafe {
688+
let ref_thread = lua.ref_thread();
689+
if (*get_userdata::<UserDataStorage<T>>(ref_thread, self.0.index)).has_exclusive_access() {
690+
take_userdata::<UserDataStorage<T>>(ref_thread, self.0.index).into_inner()
691+
} else {
692+
Err(Error::UserDataBorrowMutError)
699693
}
700-
_ => Err(Error::UserDataTypeMismatch),
701-
}
694+
},
695+
_ => Err(Error::UserDataTypeMismatch),
702696
}
703697
}
704698

src/userdata/util.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,9 +455,9 @@ pub(crate) unsafe extern "C" fn collect_userdata<T>(
455455
// It checks if the userdata is safe to destroy and sets the "destroyed" metatable
456456
// to prevent further GC collection.
457457
pub(super) unsafe extern "C-unwind" fn destroy_userdata_storage<T>(state: *mut ffi::lua_State) -> c_int {
458-
let ud = get_userdata::<UserDataStorage<T>>(state, -1);
458+
let ud = get_userdata::<UserDataStorage<T>>(state, 1);
459459
if (*ud).is_safe_to_destroy() {
460-
take_userdata::<UserDataStorage<T>>(state);
460+
take_userdata::<UserDataStorage<T>>(state, 1);
461461
ffi::lua_pushboolean(state, 1);
462462
} else {
463463
ffi::lua_pushboolean(state, 0);

src/util/userdata.rs

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -141,24 +141,27 @@ pub(crate) unsafe fn get_userdata<T>(state: *mut ffi::lua_State, index: c_int) -
141141
ud
142142
}
143143

144-
// Pops the userdata off of the top of the stack and returns it to rust, invalidating the lua
145-
// userdata and gives it the special "destructed" userdata metatable. Userdata must not have been
146-
// previously invalidated, and this method does not check for this.
147-
// Uses 1 extra stack space and does not call checkstack.
148-
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
149-
// We set the metatable of userdata on __gc to a special table with no __gc method and with
150-
// metamethods that trigger an error on access. We do this so that it will not be double
151-
// dropped, and also so that it cannot be used or identified as any particular userdata type
152-
// after the first call to __gc.
144+
/// Unwraps `T` from the Lua userdata and invalidating it by setting the special "destructed"
145+
/// metatable.
146+
///
147+
/// This method does not check that userdata is of type `T` and was not previously invalidated.
148+
///
149+
/// Uses 1 extra stack space, does not call checkstack.
150+
pub(crate) unsafe fn take_userdata<T>(state: *mut ffi::lua_State, idx: c_int) -> T {
151+
#[rustfmt::skip]
152+
let idx = if idx < 0 { ffi::lua_absindex(state, idx) } else { idx };
153+
154+
// Update the metatable of this userdata to a special one with no `__gc` method and with
155+
// metamethods that trigger an error on access.
156+
// We do this so that it will not be double dropped or used after being dropped.
153157
get_destructed_userdata_metatable(state);
154-
ffi::lua_setmetatable(state, -2);
155-
let ud = get_userdata::<T>(state, -1);
158+
ffi::lua_setmetatable(state, idx);
159+
let ud = get_userdata::<T>(state, idx);
156160

157161
// Update userdata tag to disable destructor and mark as destructed
158162
#[cfg(feature = "luau")]
159-
ffi::lua_setuserdatatag(state, -1, 1);
163+
ffi::lua_setuserdatatag(state, idx, 1);
160164

161-
ffi::lua_pop(state, 1);
162165
ptr::read(ud)
163166
}
164167

0 commit comments

Comments
 (0)