This commit is contained in:
Eric Long 2022-07-21 20:55:28 +08:00
commit a6b178328d
No known key found for this signature in database
GPG key ID: 583FAB4005C652BE
10 changed files with 93 additions and 48 deletions

View file

@ -58,7 +58,7 @@ cc = { version = "1.0" }
pkg-config = { version = "0.3.17" } pkg-config = { version = "0.3.17" }
lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true } lua-src = { version = ">= 544.0.0, < 550.0.0", optional = true }
luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true } luajit-src = { version = ">= 210.4.0, < 220.0.0", optional = true }
luau0-src = { version = "0.3.2", optional = true } luau0-src = { version = "0.3.6", optional = true }
[dev-dependencies] [dev-dependencies]
rustyline = "9.0" rustyline = "9.0"

View file

@ -324,6 +324,12 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut()) lua_tolstring(L, i, ptr::null_mut())
} }
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
// //
// Debug API // Debug API
// //

View file

@ -410,6 +410,12 @@ pub unsafe fn lua_tostring(L: *mut lua_State, i: c_int) -> *const c_char {
lua_tolstring(L, i, ptr::null_mut()) lua_tolstring(L, i, ptr::null_mut())
} }
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
// //
// Debug API // Debug API
// //

View file

@ -434,6 +434,12 @@ pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_pop(L, 1) lua_pop(L, 1)
} }
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
// //
// Debug API // Debug API
// //

View file

@ -455,6 +455,12 @@ pub unsafe fn lua_replace(L: *mut lua_State, idx: c_int) {
lua_pop(L, 1) lua_pop(L, 1)
} }
#[inline(always)]
pub unsafe fn lua_xpush(from: *mut lua_State, to: *mut lua_State, idx: c_int) {
lua_pushvalue(from, idx);
lua_xmove(from, to, 1);
}
#[inline(always)] #[inline(always)]
pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void { pub unsafe fn lua_newuserdata(L: *mut lua_State, sz: usize) -> *mut c_void {
lua_newuserdatauv(L, sz, 1) lua_newuserdatauv(L, sz, 1)

View file

@ -260,6 +260,7 @@ extern "C" {
pub fn lua_concat(L: *mut lua_State, n: c_int); pub fn lua_concat(L: *mut lua_State, n: c_int);
// TODO: lua_encodepointer // TODO: lua_encodepointer
pub fn lua_clock() -> c_double; pub fn lua_clock() -> c_double;
pub fn lua_setuserdatatag(L: *mut lua_State, idx: c_int, tag: c_int);
pub fn lua_setuserdatadtor( pub fn lua_setuserdatadtor(
L: *mut lua_State, L: *mut lua_State,
tag: c_int, tag: c_int,
@ -437,7 +438,12 @@ extern "C" {
pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char; pub fn lua_setupvalue(L: *mut lua_State, funcindex: c_int, n: c_int) -> *const c_char;
pub fn lua_singlestep(L: *mut lua_State, enabled: c_int); pub fn lua_singlestep(L: *mut lua_State, enabled: c_int);
pub fn lua_breakpoint(L: *mut lua_State, funcindex: c_int, line: c_int, enabled: c_int); pub fn lua_breakpoint(
L: *mut lua_State,
funcindex: c_int,
line: c_int,
enabled: c_int,
) -> c_int;
pub fn lua_getcoverage( pub fn lua_getcoverage(
L: *mut lua_State, L: *mut lua_State,

View file

@ -25,7 +25,7 @@ use crate::string::String;
use crate::table::Table; use crate::table::Table;
use crate::thread::Thread; use crate::thread::Thread;
use crate::types::{ use crate::types::{
Callback, CallbackUpvalue, DestructedUserdataMT, Integer, LightUserData, LuaRef, MaybeSend, Callback, CallbackUpvalue, DestructedUserdata, Integer, LightUserData, LuaRef, MaybeSend,
Number, RegistryKey, Number, RegistryKey,
}; };
use crate::userdata::{AnyUserData, UserData, UserDataCell}; use crate::userdata::{AnyUserData, UserData, UserDataCell};
@ -111,6 +111,9 @@ pub(crate) struct ExtraData {
#[cfg(feature = "async")] #[cfg(feature = "async")]
recycled_thread_cache: Vec<c_int>, recycled_thread_cache: Vec<c_int>,
// Address of `WrappedFailure` metatable
wrapped_failure_mt_ptr: *const c_void,
// Index of `Option<Waker>` userdata on the ref thread // Index of `Option<Waker>` userdata on the ref thread
#[cfg(feature = "async")] #[cfg(feature = "async")]
ref_waker_idx: c_int, ref_waker_idx: c_int,
@ -538,6 +541,13 @@ impl Lua {
"Error while creating ref thread", "Error while creating ref thread",
); );
let wrapped_failure_mt_ptr = {
get_gc_metatable::<WrappedFailure>(state);
let ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 1);
ptr
};
// Create empty Waker slot on the ref thread // Create empty Waker slot on the ref thread
#[cfg(feature = "async")] #[cfg(feature = "async")]
let ref_waker_idx = { let ref_waker_idx = {
@ -568,6 +578,7 @@ impl Lua {
multivalue_cache: Vec::with_capacity(MULTIVALUE_CACHE_SIZE), multivalue_cache: Vec::with_capacity(MULTIVALUE_CACHE_SIZE),
#[cfg(feature = "async")] #[cfg(feature = "async")]
recycled_thread_cache: Vec::new(), recycled_thread_cache: Vec::new(),
wrapped_failure_mt_ptr,
#[cfg(feature = "async")] #[cfg(feature = "async")]
ref_waker_idx, ref_waker_idx,
#[cfg(not(feature = "luau"))] #[cfg(not(feature = "luau"))]
@ -591,13 +602,13 @@ impl Lua {
"Error while storing extra data", "Error while storing extra data",
); );
// Register `DestructedUserdataMT` type // Register `DestructedUserdata` type
get_destructed_userdata_metatable(main_state); get_destructed_userdata_metatable(main_state);
let destructed_mt_ptr = ffi::lua_topointer(main_state, -1); let destructed_mt_ptr = ffi::lua_topointer(main_state, -1);
let destructed_mt_typeid = Some(TypeId::of::<DestructedUserdataMT>()); let destructed_ud_typeid = TypeId::of::<DestructedUserdata>();
(*extra.get()) (*extra.get())
.registered_userdata_mt .registered_userdata_mt
.insert(destructed_mt_ptr, destructed_mt_typeid); .insert(destructed_mt_ptr, Some(destructed_ud_typeid));
ffi::lua_pop(main_state, 1); ffi::lua_pop(main_state, 1);
mlua_debug_assert!( mlua_debug_assert!(
@ -2293,6 +2304,8 @@ impl Lua {
// Uses 2 stack spaces, does not call checkstack // Uses 2 stack spaces, does not call checkstack
pub(crate) unsafe fn pop_value(&self) -> Value { pub(crate) unsafe fn pop_value(&self) -> Value {
let state = self.state; let state = self.state;
let extra = &mut *self.extra.get();
match ffi::lua_type(state, -1) { match ffi::lua_type(state, -1) {
ffi::LUA_TNIL => { ffi::LUA_TNIL => {
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
@ -2353,9 +2366,11 @@ impl Lua {
ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())), ffi::LUA_TFUNCTION => Value::Function(Function(self.pop_ref())),
ffi::LUA_TUSERDATA => { ffi::LUA_TUSERDATA => {
let wrapped_failure_mt_ptr = extra.wrapped_failure_mt_ptr;
// We must prevent interaction with userdata types other than UserData OR a WrappedError. // We must prevent interaction with userdata types other than UserData OR a WrappedError.
// WrappedPanics are automatically resumed. // WrappedPanics are automatically resumed.
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() { match get_gc_userdata::<WrappedFailure>(state, -1, wrapped_failure_mt_ptr).as_mut()
{
Some(WrappedFailure::Error(err)) => { Some(WrappedFailure::Error(err)) => {
let err = err.clone(); let err = err.clone();
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
@ -2394,12 +2409,6 @@ impl Lua {
"Lua instance passed Value created from a different main Lua state" "Lua instance passed Value created from a different main Lua state"
); );
let extra = &*self.extra.get(); let extra = &*self.extra.get();
#[cfg(not(feature = "luau"))]
{
ffi::lua_pushvalue(extra.ref_thread, lref.index);
ffi::lua_xmove(extra.ref_thread, self.state, 1);
}
#[cfg(feature = "luau")]
ffi::lua_xpush(extra.ref_thread, self.state, lref.index); ffi::lua_xpush(extra.ref_thread, self.state, lref.index);
} }
@ -2578,7 +2587,7 @@ impl Lua {
let extra = &*self.extra.get(); let extra = &*self.extra.get();
match extra.registered_userdata_mt.get(&mt_ptr) { match extra.registered_userdata_mt.get(&mt_ptr) {
Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdataMT>()) => { Some(&type_id) if type_id == Some(TypeId::of::<DestructedUserdata>()) => {
Err(Error::UserDataDestructed) Err(Error::UserDataDestructed)
} }
Some(&type_id) => Ok(type_id), Some(&type_id) => Ok(type_id),
@ -2952,14 +2961,14 @@ struct StateGuard<'a>(&'a mut LuaInner, *mut ffi::lua_State);
impl<'a> StateGuard<'a> { impl<'a> StateGuard<'a> {
fn new(inner: &'a mut LuaInner, mut state: *mut ffi::lua_State) -> Self { fn new(inner: &'a mut LuaInner, mut state: *mut ffi::lua_State) -> Self {
mem::swap(&mut (*inner).state, &mut state); mem::swap(&mut inner.state, &mut state);
Self(inner, state) Self(inner, state)
} }
} }
impl<'a> Drop for StateGuard<'a> { impl<'a> Drop for StateGuard<'a> {
fn drop(&mut self) { fn drop(&mut self) {
mem::swap(&mut (*self.0).state, &mut self.1); mem::swap(&mut self.0.state, &mut self.1);
} }
} }

View file

@ -83,7 +83,7 @@ pub trait MaybeSend {}
#[cfg(not(feature = "send"))] #[cfg(not(feature = "send"))]
impl<T> MaybeSend for T {} impl<T> MaybeSend for T {}
pub(crate) struct DestructedUserdataMT; pub(crate) struct DestructedUserdata;
/// An auto generated key into the Lua registry. /// An auto generated key into the Lua registry.
/// ///

View file

@ -1,6 +1,7 @@
use std::any::{Any, TypeId}; use std::any::{Any, TypeId};
use std::ffi::CStr; use std::ffi::CStr;
use std::fmt::Write; use std::fmt::Write;
use std::mem::MaybeUninit;
use std::os::raw::{c_char, c_int, c_void}; use std::os::raw::{c_char, c_int, c_void};
use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe};
use std::sync::Arc; use std::sync::Arc;
@ -136,26 +137,21 @@ where
F: Fn(*mut ffi::lua_State) -> R, F: Fn(*mut ffi::lua_State) -> R,
R: Copy, R: Copy,
{ {
union URes<R: Copy> {
uninit: (),
init: R,
}
struct Params<F, R: Copy> { struct Params<F, R: Copy> {
function: F, function: F,
result: URes<R>, result: MaybeUninit<R>,
nresults: c_int, nresults: c_int,
} }
unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int unsafe extern "C" fn do_call<F, R>(state: *mut ffi::lua_State) -> c_int
where where
R: Copy,
F: Fn(*mut ffi::lua_State) -> R, F: Fn(*mut ffi::lua_State) -> R,
R: Copy,
{ {
let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>; let params = ffi::lua_touserdata(state, -1) as *mut Params<F, R>;
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
(*params).result.init = ((*params).function)(state); (*params).result.write(((*params).function)(state));
if (*params).nresults == ffi::LUA_MULTRET { if (*params).nresults == ffi::LUA_MULTRET {
ffi::lua_gettop(state) ffi::lua_gettop(state)
@ -174,7 +170,7 @@ where
let mut params = Params { let mut params = Params {
function: f, function: f,
result: URes { uninit: () }, result: MaybeUninit::uninit(),
nresults, nresults,
}; };
@ -185,7 +181,7 @@ where
if ret == ffi::LUA_OK { if ret == ffi::LUA_OK {
// `LUA_OK` is only returned when the `do_call` function has completed successfully, so // `LUA_OK` is only returned when the `do_call` function has completed successfully, so
// `params.result` is definitely initialized. // `params.result` is definitely initialized.
Ok(params.result.init) Ok(params.result.assume_init())
} else { } else {
Err(pop_error(state, ret)) Err(pop_error(state, ret))
} }
@ -203,7 +199,7 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error {
"pop_error called with non-error return code" "pop_error called with non-error return code"
); );
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() { match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_mut() {
Some(WrappedFailure::Error(err)) => { Some(WrappedFailure::Error(err)) => {
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
err.clone() err.clone()
@ -314,10 +310,7 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool)
#[inline] #[inline]
pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> { pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool) -> Result<()> {
unsafe extern "C" fn destructor<T>(ud: *mut c_void) { unsafe extern "C" fn destructor<T>(ud: *mut c_void) {
let ud = ud as *mut T; ptr::drop_in_place(ud as *mut T);
if *(ud.offset(1) as *mut u8) == 0 {
ptr::drop_in_place(ud);
}
} }
let size = mem::size_of::<T>() + 1; let size = mem::size_of::<T>() + 1;
@ -329,7 +322,6 @@ pub unsafe fn push_userdata<T>(state: *mut ffi::lua_State, t: T, protect: bool)
ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T ffi::lua_newuserdatadtor(state, size, destructor::<T>) as *mut T
}; };
ptr::write(ud, t); ptr::write(ud, t);
*(ud.offset(1) as *mut u8) = 0; // Mark as not destructed
Ok(()) Ok(())
} }
@ -373,10 +365,12 @@ pub unsafe fn take_userdata<T>(state: *mut ffi::lua_State) -> T {
get_destructed_userdata_metatable(state); get_destructed_userdata_metatable(state);
ffi::lua_setmetatable(state, -2); ffi::lua_setmetatable(state, -2);
let ud = get_userdata::<T>(state, -1); let ud = get_userdata::<T>(state, -1);
// Update userdata tag to disable destructor and mark as destructed
#[cfg(feature = "luau")]
ffi::lua_setuserdatatag(state, -1, 1);
ffi::lua_pop(state, 1); ffi::lua_pop(state, 1);
if cfg!(feature = "luau") {
*(ud.offset(1) as *mut u8) = 1; // Mark as destructed
}
ptr::read(ud) ptr::read(ud)
} }
@ -394,16 +388,28 @@ pub unsafe fn push_gc_userdata<T: Any>(
} }
// Uses 2 stack spaces, does not call checkstack // Uses 2 stack spaces, does not call checkstack
pub unsafe fn get_gc_userdata<T: Any>(state: *mut ffi::lua_State, index: c_int) -> *mut T { pub unsafe fn get_gc_userdata<T: Any>(
state: *mut ffi::lua_State,
index: c_int,
mt_ptr: *const c_void,
) -> *mut T {
let ud = ffi::lua_touserdata(state, index) as *mut T; let ud = ffi::lua_touserdata(state, index) as *mut T;
if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 { if ud.is_null() || ffi::lua_getmetatable(state, index) == 0 {
return ptr::null_mut(); return ptr::null_mut();
} }
get_gc_metatable::<T>(state); if !mt_ptr.is_null() {
let res = ffi::lua_rawequal(state, -1, -2); let ud_mt_ptr = ffi::lua_topointer(state, -1);
ffi::lua_pop(state, 2); ffi::lua_pop(state, 1);
if res == 0 { if !ptr::eq(ud_mt_ptr, mt_ptr) {
return ptr::null_mut(); return ptr::null_mut();
}
} else {
get_gc_metatable::<T>(state);
let res = ffi::lua_rawequal(state, -1, -2);
ffi::lua_pop(state, 2);
if res == 0 {
return ptr::null_mut();
}
} }
ud ud
} }
@ -679,7 +685,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
return 1; return 1;
} }
if get_gc_userdata::<WrappedFailure>(state, -1).is_null() { if get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).is_null() {
let s = ffi::luaL_tolstring(state, -1, ptr::null_mut()); let s = ffi::luaL_tolstring(state, -1, ptr::null_mut());
if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 { if ffi::lua_checkstack(state, ffi::LUA_TRACEBACK_STACK) != 0 {
ffi::luaL_traceback(state, state, s, 0); ffi::luaL_traceback(state, state, s, 0);
@ -706,7 +712,7 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettop(state) ffi::lua_gettop(state)
} else { } else {
if let Some(WrappedFailure::Panic(_)) = if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1).as_ref() get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{ {
ffi::lua_error(state); ffi::lua_error(state);
} }
@ -722,7 +728,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
ffi::luaL_checkstack(state, 2, ptr::null()); ffi::luaL_checkstack(state, 2, ptr::null());
if let Some(WrappedFailure::Panic(_)) = if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1).as_ref() get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{ {
1 1
} else { } else {
@ -752,7 +758,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
ffi::lua_gettop(state) - 1 ffi::lua_gettop(state) - 1
} else { } else {
if let Some(WrappedFailure::Panic(_)) = if let Some(WrappedFailure::Panic(_)) =
get_gc_userdata::<WrappedFailure>(state, -1).as_ref() get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref()
{ {
ffi::lua_error(state); ffi::lua_error(state);
} }
@ -836,7 +842,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
callback_error(state, |_| { callback_error(state, |_| {
check_stack(state, 3)?; check_stack(state, 3)?;
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1).as_ref() { let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1, ptr::null()).as_ref() {
Some(WrappedFailure::Error(error)) => { Some(WrappedFailure::Error(error)) => {
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void; let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key); ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);

View file

@ -239,7 +239,7 @@ impl<'a, 'lua> IntoIterator for &'a MultiValue<'lua> {
#[inline] #[inline]
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
(&self.0).iter().rev() self.0.iter().rev()
} }
} }