Allow to catch rust panics via pcall
This commit is contained in:
parent
b23ee6a162
commit
0c230a3037
20
src/lua.rs
20
src/lua.rs
|
@ -19,8 +19,8 @@ use crate::userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
assert_stack, callback_error, check_stack, get_userdata, get_wrapped_error,
|
assert_stack, callback_error, check_stack, get_userdata, get_wrapped_error,
|
||||||
init_error_registry, init_userdata_metatable, main_state, pop_error, protect_lua,
|
init_error_registry, init_userdata_metatable, main_state, pop_error, protect_lua,
|
||||||
protect_lua_closure, push_string, push_userdata, push_wrapped_error, safe_pcall, safe_xpcall,
|
protect_lua_closure, push_string, push_userdata, push_wrapped_error, userdata_destructor,
|
||||||
userdata_destructor, StackGuard,
|
StackGuard,
|
||||||
};
|
};
|
||||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
|
@ -62,22 +62,6 @@ impl Lua {
|
||||||
|
|
||||||
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
|
||||||
// Override pcall and xpcall with versions that cannot be used to catch rust panics.
|
|
||||||
|
|
||||||
/*
|
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("pcall"));
|
|
||||||
ffi::lua_pushcfunction(state, safe_pcall);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("xpcall"));
|
|
||||||
ffi::lua_pushcfunction(state, safe_xpcall);
|
|
||||||
ffi::lua_rawset(state, -3);
|
|
||||||
|
|
||||||
ffi::lua_pop(state, 1);
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
// Create ref stack thread and place it in the registry to prevent it from being garbage
|
||||||
// collected.
|
// collected.
|
||||||
|
|
||||||
|
|
99
src/util.rs
99
src/util.rs
|
@ -372,8 +372,7 @@ where
|
||||||
|
|
||||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||||
Ok(Ok(r)) => {
|
Ok(Ok(r)) => {
|
||||||
ffi::lua_rotate(state, 1, -1);
|
ffi::lua_remove(state, 1);
|
||||||
ffi::lua_pop(state, 1);
|
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
|
@ -442,71 +441,6 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||||
1
|
1
|
||||||
}
|
}
|
||||||
|
|
||||||
// A variant of pcall that does not allow lua to catch panic errors from callback_error
|
|
||||||
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
||||||
|
|
||||||
let top = ffi::lua_gettop(state);
|
|
||||||
if top == 0 {
|
|
||||||
ffi::lua_pushstring(state, cstr!("not enough arguments to pcall"));
|
|
||||||
ffi::lua_error(state);
|
|
||||||
} else if ffi::lua_pcall(state, top - 1, ffi::LUA_MULTRET, 0) != ffi::LUA_OK {
|
|
||||||
if is_wrapped_panic(state, -1) {
|
|
||||||
ffi::lua_error(state);
|
|
||||||
}
|
|
||||||
ffi::lua_pushboolean(state, 0);
|
|
||||||
ffi::lua_insert(state, -2);
|
|
||||||
2
|
|
||||||
} else {
|
|
||||||
ffi::lua_pushboolean(state, 1);
|
|
||||||
ffi::lua_insert(state, 1);
|
|
||||||
ffi::lua_gettop(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A variant of xpcall that does not allow lua to catch panic errors from callback_error
|
|
||||||
pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
|
||||||
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
|
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
||||||
|
|
||||||
if is_wrapped_panic(state, -1) {
|
|
||||||
1
|
|
||||||
} else {
|
|
||||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
|
||||||
ffi::lua_insert(state, 1);
|
|
||||||
ffi::lua_call(state, ffi::lua_gettop(state) - 1, ffi::LUA_MULTRET);
|
|
||||||
ffi::lua_gettop(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
|
||||||
|
|
||||||
let top = ffi::lua_gettop(state);
|
|
||||||
if top < 2 {
|
|
||||||
ffi::lua_pushstring(state, cstr!("not enough arguments to xpcall"));
|
|
||||||
ffi::lua_error(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
ffi::lua_pushvalue(state, 2);
|
|
||||||
ffi::lua_pushcclosure(state, xpcall_msgh, 1);
|
|
||||||
ffi::lua_copy(state, 1, 2);
|
|
||||||
ffi::lua_replace(state, 1);
|
|
||||||
|
|
||||||
let res = ffi::lua_pcall(state, ffi::lua_gettop(state) - 2, ffi::LUA_MULTRET, 1);
|
|
||||||
if res != ffi::LUA_OK {
|
|
||||||
if is_wrapped_panic(state, -1) {
|
|
||||||
ffi::lua_error(state);
|
|
||||||
}
|
|
||||||
ffi::lua_pushboolean(state, 0);
|
|
||||||
ffi::lua_insert(state, -2);
|
|
||||||
2
|
|
||||||
} else {
|
|
||||||
ffi::lua_pushboolean(state, 1);
|
|
||||||
ffi::lua_insert(state, 2);
|
|
||||||
ffi::lua_gettop(state) - 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does not call lua_checkstack, uses 1 stack space.
|
// Does not call lua_checkstack, uses 1 stack space.
|
||||||
pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
pub unsafe fn main_state(state: *mut ffi::lua_State) -> *mut ffi::lua_State {
|
||||||
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_MAINTHREAD);
|
||||||
|
@ -574,6 +508,31 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||||
// kind of recursive error structure?)
|
// kind of recursive error structure?)
|
||||||
let _ = write!(&mut (*err_buf), "{}", error);
|
let _ = write!(&mut (*err_buf), "{}", error);
|
||||||
Ok(err_buf)
|
Ok(err_buf)
|
||||||
|
} else if is_wrapped_panic(state, -1) {
|
||||||
|
let panic = get_userdata::<WrappedPanic>(state, -1);
|
||||||
|
if let Some(p) = (*panic).0.take() {
|
||||||
|
ffi::lua_pushlightuserdata(
|
||||||
|
state,
|
||||||
|
&ERROR_PRINT_BUFFER_KEY as *const u8 as *mut c_void,
|
||||||
|
);
|
||||||
|
ffi::lua_rawget(state, ffi::LUA_REGISTRYINDEX);
|
||||||
|
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||||
|
ffi::lua_pop(state, 2);
|
||||||
|
|
||||||
|
let error = if let Some(x) = p.downcast_ref::<&str>() {
|
||||||
|
x.to_string()
|
||||||
|
} else if let Some(x) = p.downcast_ref::<String>() {
|
||||||
|
x.to_string()
|
||||||
|
} else {
|
||||||
|
"panic".to_string()
|
||||||
|
};
|
||||||
|
|
||||||
|
(*err_buf).clear();
|
||||||
|
let _ = write!(&mut (*err_buf), "{}", error);
|
||||||
|
Ok(err_buf)
|
||||||
|
} else {
|
||||||
|
rlua_panic!("error during panic handling, panic was resumed twice")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// I'm not sure whether this is possible to trigger without bugs in rlua?
|
// I'm not sure whether this is possible to trigger without bugs in rlua?
|
||||||
Err(Error::UserDataTypeMismatch)
|
Err(Error::UserDataTypeMismatch)
|
||||||
|
@ -621,6 +580,10 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||||
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedPanic>);
|
ffi::lua_pushcfunction(state, userdata_destructor::<WrappedPanic>);
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
|
ffi::lua_pushstring(state, cstr!("__tostring"));
|
||||||
|
ffi::lua_pushcfunction(state, error_tostring);
|
||||||
|
ffi::lua_rawset(state, -3);
|
||||||
|
|
||||||
ffi::lua_pushstring(state, cstr!("__metatable"));
|
ffi::lua_pushstring(state, cstr!("__metatable"));
|
||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
ffi::lua_rawset(state, -3);
|
ffi::lua_rawset(state, -3);
|
||||||
|
@ -696,7 +659,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct WrappedError(pub Error);
|
struct WrappedError(pub Error);
|
||||||
struct WrappedPanic(pub Option<Box<dyn Any + Send>>);
|
struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
||||||
|
|
||||||
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
// Converts the given lua value to a string in a reasonable format without causing a Lua error or
|
||||||
// panicking.
|
// panicking.
|
||||||
|
|
Loading…
Reference in New Issue