Small improvements to WrappedError
Currently trying to figure out how to make LuaError a proper LuaUserDataType
This commit is contained in:
parent
3deb6df525
commit
1fda34225e
226
src/util.rs
226
src/util.rs
|
@ -121,52 +121,49 @@ where
|
||||||
pub unsafe fn handle_error(state: *mut ffi::lua_State, err: c_int) -> LuaResult<()> {
|
pub unsafe fn handle_error(state: *mut ffi::lua_State, err: c_int) -> LuaResult<()> {
|
||||||
if err == ffi::LUA_OK || err == ffi::LUA_YIELD {
|
if err == ffi::LUA_OK || err == ffi::LUA_YIELD {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
} else if let Some(error) = pop_wrapped_error(state) {
|
||||||
|
Err(error)
|
||||||
} else {
|
} else {
|
||||||
if is_wrapped_error(state, -1) {
|
let err_string = if let Some(s) = ffi::lua_tolstring(state, -1, ptr::null_mut()).as_ref() {
|
||||||
Err(pop_wrapped_error(state))
|
CStr::from_ptr(s).to_str().unwrap().to_owned()
|
||||||
} else {
|
} else {
|
||||||
let err_string =
|
"<unprintable error>".to_owned()
|
||||||
if let Some(s) = ffi::lua_tolstring(state, -1, ptr::null_mut()).as_ref() {
|
};
|
||||||
CStr::from_ptr(s).to_str().unwrap().to_owned()
|
ffi::lua_pop(state, 1);
|
||||||
} else {
|
|
||||||
"<unprintable error>".to_owned()
|
|
||||||
};
|
|
||||||
ffi::lua_pop(state, 1);
|
|
||||||
|
|
||||||
Err(match err {
|
Err(match err {
|
||||||
ffi::LUA_ERRRUN => LuaError::RuntimeError(err_string),
|
ffi::LUA_ERRRUN => LuaError::RuntimeError(err_string),
|
||||||
ffi::LUA_ERRSYNTAX => {
|
ffi::LUA_ERRSYNTAX => {
|
||||||
// This seems terrible, but as far as I can tell, this is exactly what the stock lua
|
// This seems terrible, but as far as I can tell, this is exactly what the stock lua
|
||||||
// repl does.
|
// repl does.
|
||||||
if err_string.ends_with("<eof>") {
|
if err_string.ends_with("<eof>") {
|
||||||
LuaSyntaxError::IncompleteStatement(err_string).into()
|
LuaSyntaxError::IncompleteStatement(err_string).into()
|
||||||
} else {
|
} else {
|
||||||
LuaSyntaxError::Syntax(err_string).into()
|
LuaSyntaxError::Syntax(err_string).into()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ffi::LUA_ERRERR => LuaError::ErrorError(err_string),
|
}
|
||||||
ffi::LUA_ERRMEM => {
|
ffi::LUA_ERRERR => LuaError::ErrorError(err_string),
|
||||||
// This is not impossible to hit, but this library is not set up
|
ffi::LUA_ERRMEM => {
|
||||||
// to handle this properly. Lua does a longjmp on out of memory
|
// This is not impossible to hit, but this library is not set up
|
||||||
// (like all lua errors), but it can do this from a huge number
|
// to handle this properly. Lua does a longjmp on out of memory
|
||||||
// of lua functions, and it is extremely difficult to set up the
|
// (like all lua errors), but it can do this from a huge number
|
||||||
// pcall protection for every lua function that might allocate.
|
// of lua functions, and it is extremely difficult to set up the
|
||||||
// If lua does this in an unprotected context, it will abort
|
// pcall protection for every lua function that might allocate.
|
||||||
// anyway, so the best we can do right now is guarantee an abort
|
// If lua does this in an unprotected context, it will abort
|
||||||
// even in a protected context.
|
// anyway, so the best we can do right now is guarantee an abort
|
||||||
println!("Lua memory error, aborting!");
|
// even in a protected context.
|
||||||
process::abort()
|
println!("Lua memory error, aborting!");
|
||||||
}
|
process::abort()
|
||||||
ffi::LUA_ERRGCMM => {
|
}
|
||||||
// This should be impossible, or at least is indicative of an
|
ffi::LUA_ERRGCMM => {
|
||||||
// internal bug. Similarly to LUA_ERRMEM, this could indicate a
|
// This should be impossible, or at least is indicative of an
|
||||||
// longjmp out of rust code, so we just abort.
|
// internal bug. Similarly to LUA_ERRMEM, this could indicate a
|
||||||
println!("Lua error during __gc, aborting!");
|
// longjmp out of rust code, so we just abort.
|
||||||
process::abort()
|
println!("Lua error during __gc, aborting!");
|
||||||
}
|
process::abort()
|
||||||
_ => panic!("unrecognized lua error code"),
|
}
|
||||||
})
|
_ => panic!("unrecognized lua error code"),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,10 +185,11 @@ pub unsafe extern "C" fn destructor<T>(state: *mut ffi::lua_State) -> c_int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In the context of a lua callback, this will call the given function and if the given function
|
// In the context of a lua callback, this will call the given function and if
|
||||||
// returns an error, *or if the given function panics*, this will result in a call to lua_error (a
|
// the given function returns an error, *or if the given function panics*, this
|
||||||
// longjmp). The error or panic is wrapped in such a way that when calling pop_error back on
|
// will result in a call to lua_error (a longjmp). Panics are wrapped in such a
|
||||||
// the rust side, it will resume the panic.
|
// way that when calling handle_error back on the rust side, it will resume the
|
||||||
|
// panic.
|
||||||
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
pub unsafe fn callback_error<R, F>(state: *mut ffi::lua_State, f: F) -> R
|
||||||
where
|
where
|
||||||
F: FnOnce() -> LuaResult<R> + UnwindSafe,
|
F: FnOnce() -> LuaResult<R> + UnwindSafe,
|
||||||
|
@ -219,31 +217,33 @@ pub unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Se
|
||||||
do_push_wrapped_error(state, WrappedError::Panic(Some(panic)));
|
do_push_wrapped_error(state, WrappedError::Panic(Some(panic)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pops a WrappedError off of the top of the stack, if it is a WrappedError::Error, returns it, if
|
// If a WrappedError is at the top of the lua stack, pops it off. If the
|
||||||
// it is a WrappedError::Panic, clears the current stack and panics.
|
// WrappedError was a WrappedError::Panic, clears the lua stack and resumes the
|
||||||
pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> LuaError {
|
// panic, otherwise returns the WrappedError::Error. If the value at the top of
|
||||||
|
// the stack was not a WrappedError, returns None and does not pop the value.
|
||||||
|
pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> Option<LuaError> {
|
||||||
assert!(
|
assert!(
|
||||||
ffi::lua_gettop(state) > 0,
|
ffi::lua_gettop(state) > 0,
|
||||||
"pop_wrapped_error called with nothing on the stack"
|
"pop_wrapped_error called with nothing on the stack"
|
||||||
);
|
);
|
||||||
assert!(
|
|
||||||
is_wrapped_error(state, -1),
|
|
||||||
"pop_wrapped_error called when the top of the stack is not a WrappedError"
|
|
||||||
);
|
|
||||||
|
|
||||||
let userdata = ffi::lua_touserdata(state, -1);
|
if wrapped_error_type(state, -1) == WrappedErrorType::NotWrappedError {
|
||||||
match &mut *(userdata as *mut WrappedError) {
|
None
|
||||||
&mut WrappedError::Error(ref err) => {
|
} else {
|
||||||
let err = err.clone();
|
let userdata = ffi::lua_touserdata(state, -1);
|
||||||
ffi::lua_pop(state, 1);
|
match &mut *(userdata as *mut WrappedError) {
|
||||||
err
|
&mut WrappedError::Error(ref err) => {
|
||||||
}
|
let err = err.clone();
|
||||||
&mut WrappedError::Panic(ref mut p) => {
|
ffi::lua_pop(state, 1);
|
||||||
let p = p.take().unwrap_or_else(|| {
|
Some(err)
|
||||||
Box::new("internal error: panic error used twice")
|
}
|
||||||
});
|
&mut WrappedError::Panic(ref mut p) => {
|
||||||
ffi::lua_settop(state, 0);
|
let p = p.take().unwrap_or_else(|| {
|
||||||
resume_unwind(p)
|
Box::new("internal error: panic error used twice")
|
||||||
|
});
|
||||||
|
ffi::lua_settop(state, 0);
|
||||||
|
resume_unwind(p)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,9 +257,10 @@ pub unsafe fn pcall_with_traceback(
|
||||||
nresults: c_int,
|
nresults: c_int,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
unsafe extern "C" fn message_handler(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn message_handler(state: *mut ffi::lua_State) -> c_int {
|
||||||
if is_wrapped_error(state, 1) {
|
match wrapped_error_type(state, 1) {
|
||||||
if !is_panic_error(state, 1) {
|
WrappedErrorType::Panic => {}
|
||||||
let error = pop_wrapped_error(state);
|
WrappedErrorType::Error => {
|
||||||
|
let error = pop_wrapped_error(state).unwrap();
|
||||||
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
ffi::luaL_traceback(state, state, ptr::null(), 0);
|
||||||
let traceback = CStr::from_ptr(ffi::lua_tolstring(state, -1, ptr::null_mut()))
|
let traceback = CStr::from_ptr(ffi::lua_tolstring(state, -1, ptr::null_mut()))
|
||||||
.to_str()
|
.to_str()
|
||||||
|
@ -267,12 +268,13 @@ pub unsafe fn pcall_with_traceback(
|
||||||
.to_owned();
|
.to_owned();
|
||||||
push_wrapped_error(state, LuaError::CallbackError(traceback, Arc::new(error)));
|
push_wrapped_error(state, LuaError::CallbackError(traceback, Arc::new(error)));
|
||||||
}
|
}
|
||||||
} else {
|
WrappedErrorType::NotWrappedError => {
|
||||||
let s = ffi::lua_tolstring(state, 1, ptr::null_mut());
|
let s = ffi::lua_tolstring(state, 1, ptr::null_mut());
|
||||||
if !s.is_null() {
|
if !s.is_null() {
|
||||||
ffi::luaL_traceback(state, state, s, 0);
|
ffi::luaL_traceback(state, state, s, 0);
|
||||||
} else {
|
} else {
|
||||||
ffi::luaL_traceback(state, state, cstr!("<unprintable lua error>"), 0);
|
ffi::luaL_traceback(state, state, cstr!("<unprintable lua error>"), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
1
|
1
|
||||||
|
@ -293,9 +295,10 @@ pub unsafe fn resume_with_traceback(
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
let res = ffi::lua_resume(state, from, nargs);
|
let res = ffi::lua_resume(state, from, nargs);
|
||||||
if res != ffi::LUA_OK && res != ffi::LUA_YIELD {
|
if res != ffi::LUA_OK && res != ffi::LUA_YIELD {
|
||||||
if is_wrapped_error(state, 1) {
|
match wrapped_error_type(state, 1) {
|
||||||
if !is_panic_error(state, 1) {
|
WrappedErrorType::Panic => {}
|
||||||
let error = pop_wrapped_error(state);
|
WrappedErrorType::Error => {
|
||||||
|
let error = pop_wrapped_error(state).unwrap();
|
||||||
ffi::luaL_traceback(from, state, ptr::null(), 0);
|
ffi::luaL_traceback(from, state, ptr::null(), 0);
|
||||||
let traceback = CStr::from_ptr(ffi::lua_tolstring(from, -1, ptr::null_mut()))
|
let traceback = CStr::from_ptr(ffi::lua_tolstring(from, -1, ptr::null_mut()))
|
||||||
.to_str()
|
.to_str()
|
||||||
|
@ -303,12 +306,13 @@ pub unsafe fn resume_with_traceback(
|
||||||
.to_owned();
|
.to_owned();
|
||||||
push_wrapped_error(from, LuaError::CallbackError(traceback, Arc::new(error)));
|
push_wrapped_error(from, LuaError::CallbackError(traceback, Arc::new(error)));
|
||||||
}
|
}
|
||||||
} else {
|
WrappedErrorType::NotWrappedError => {
|
||||||
let s = ffi::lua_tolstring(state, 1, ptr::null_mut());
|
let s = ffi::lua_tolstring(state, 1, ptr::null_mut());
|
||||||
if !s.is_null() {
|
if !s.is_null() {
|
||||||
ffi::luaL_traceback(from, state, s, 0);
|
ffi::luaL_traceback(from, state, s, 0);
|
||||||
} else {
|
} else {
|
||||||
ffi::luaL_traceback(from, state, cstr!("<unprintable lua error>"), 0);
|
ffi::luaL_traceback(from, state, cstr!("<unprintable lua error>"), 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -318,7 +322,7 @@ pub unsafe fn resume_with_traceback(
|
||||||
// A variant of pcall that does not allow lua to catch panic errors from callback_error
|
// 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 {
|
pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
if ffi::lua_pcall(state, ffi::lua_gettop(state) - 1, ffi::LUA_MULTRET, 0) != ffi::LUA_OK {
|
if ffi::lua_pcall(state, ffi::lua_gettop(state) - 1, ffi::LUA_MULTRET, 0) != ffi::LUA_OK {
|
||||||
if is_panic_error(state, -1) {
|
if wrapped_error_type(state, -1) == WrappedErrorType::Panic {
|
||||||
ffi::lua_error(state);
|
ffi::lua_error(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -328,7 +332,7 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
// A variant of xpcall that does not allow lua to catch panic errors from callback_error
|
// 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 {
|
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 {
|
unsafe extern "C" fn xpcall_msgh(state: *mut ffi::lua_State) -> c_int {
|
||||||
if is_panic_error(state, -1) {
|
if wrapped_error_type(state, -1) == WrappedErrorType::Panic {
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||||
|
@ -345,7 +349,7 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
|
|
||||||
let res = ffi::lua_pcall(state, ffi::lua_gettop(state) - 2, ffi::LUA_MULTRET, 1);
|
let res = ffi::lua_pcall(state, ffi::lua_gettop(state) - 2, ffi::LUA_MULTRET, 1);
|
||||||
if res != ffi::LUA_OK {
|
if res != ffi::LUA_OK {
|
||||||
if is_panic_error(state, -1) {
|
if wrapped_error_type(state, -1) == WrappedErrorType::Panic {
|
||||||
ffi::lua_error(state);
|
ffi::lua_error(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -374,7 +378,7 @@ unsafe fn do_push_wrapped_error(state: *mut ffi::lua_State, err: WrappedError) {
|
||||||
|
|
||||||
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int {
|
||||||
callback_error(state, || {
|
callback_error(state, || {
|
||||||
if !is_wrapped_error(state, -1) {
|
if wrapped_error_type(state, -1) == WrappedErrorType::NotWrappedError {
|
||||||
panic!("error metatable on wrong userdata, impossible")
|
panic!("error metatable on wrong userdata, impossible")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,63 +438,45 @@ unsafe fn do_push_wrapped_error(state: *mut ffi::lua_State, err: WrappedError) {
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the error at the given index is a WrappedError::Panic
|
#[derive(Eq, PartialEq)]
|
||||||
unsafe fn is_panic_error(state: *mut ffi::lua_State, index: c_int) -> bool {
|
enum WrappedErrorType {
|
||||||
|
NotWrappedError,
|
||||||
|
Error,
|
||||||
|
Panic,
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn wrapped_error_type(state: *mut ffi::lua_State, index: c_int) -> WrappedErrorType {
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
ffi::lua_checkstack(state, 2),
|
ffi::lua_checkstack(state, 2),
|
||||||
0,
|
0,
|
||||||
"somehow not enough stack space to check if an error is panic"
|
"somehow not enough stack space to check wrapped error type"
|
||||||
);
|
);
|
||||||
|
|
||||||
let index = ffi::lua_absindex(state, index);
|
let index = ffi::lua_absindex(state, index);
|
||||||
|
|
||||||
let userdata = ffi::lua_touserdata(state, index);
|
let userdata = ffi::lua_touserdata(state, index);
|
||||||
if userdata.is_null() {
|
if userdata.is_null() {
|
||||||
return false;
|
return WrappedErrorType::NotWrappedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ffi::lua_getmetatable(state, index) == 0 {
|
if ffi::lua_getmetatable(state, index) == 0 {
|
||||||
return false;
|
return WrappedErrorType::NotWrappedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_error_metatable(state);
|
get_error_metatable(state);
|
||||||
if ffi::lua_rawequal(state, -1, -2) == 0 {
|
if ffi::lua_rawequal(state, -1, -2) == 0 {
|
||||||
ffi::lua_pop(state, 2);
|
ffi::lua_pop(state, 2);
|
||||||
return false;
|
return WrappedErrorType::NotWrappedError;
|
||||||
}
|
}
|
||||||
|
|
||||||
ffi::lua_pop(state, 2);
|
ffi::lua_pop(state, 2);
|
||||||
|
|
||||||
match &*(userdata as *const WrappedError) {
|
match &*(userdata as *const WrappedError) {
|
||||||
&WrappedError::Error(_) => false,
|
&WrappedError::Error(_) => WrappedErrorType::Error,
|
||||||
&WrappedError::Panic(_) => true,
|
&WrappedError::Panic(_) => WrappedErrorType::Panic,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn is_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> bool {
|
|
||||||
assert_ne!(
|
|
||||||
ffi::lua_checkstack(state, 2),
|
|
||||||
0,
|
|
||||||
"somehow not enough stack space to check if an error is rrapped"
|
|
||||||
);
|
|
||||||
|
|
||||||
let index = ffi::lua_absindex(state, index);
|
|
||||||
|
|
||||||
let userdata = ffi::lua_touserdata(state, index);
|
|
||||||
if userdata.is_null() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ffi::lua_getmetatable(state, index) == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
get_error_metatable(state);
|
|
||||||
let res = ffi::lua_rawequal(state, -1, -2) != 0;
|
|
||||||
ffi::lua_pop(state, 2);
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
|
unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_pushlightuserdata(
|
ffi::lua_pushlightuserdata(
|
||||||
state,
|
state,
|
||||||
|
|
Loading…
Reference in New Issue