Combine WrappedError and WrappedPanic structs to WrappedFailure enum
This commit is contained in:
parent
582b2c585f
commit
3bffe1d7c3
107
src/lua.rs
107
src/lua.rs
|
@ -27,10 +27,10 @@ use crate::userdata::{
|
||||||
};
|
};
|
||||||
use crate::util::{
|
use crate::util::{
|
||||||
self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable,
|
self, assert_stack, callback_error, check_stack, get_destructed_userdata_metatable,
|
||||||
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, get_wrapped_error,
|
get_gc_metatable, get_gc_userdata, get_main_state, get_userdata, init_error_registry,
|
||||||
init_error_registry, init_gc_metatable, init_userdata_metatable, pop_error, protect_lua,
|
init_gc_metatable, init_userdata_metatable, pop_error, protect_lua, push_gc_userdata,
|
||||||
push_gc_userdata, push_string, push_table, push_userdata, push_wrapped_error, rawset_field,
|
push_string, push_table, push_userdata, rawset_field, safe_pcall, safe_xpcall, StackGuard,
|
||||||
safe_pcall, safe_xpcall, StackGuard, WrappedError, WrappedPanic,
|
WrappedFailure,
|
||||||
};
|
};
|
||||||
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
use crate::value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value};
|
||||||
|
|
||||||
|
@ -77,9 +77,9 @@ struct ExtraData {
|
||||||
ref_stack_top: c_int,
|
ref_stack_top: c_int,
|
||||||
ref_free: Vec<c_int>,
|
ref_free: Vec<c_int>,
|
||||||
|
|
||||||
// Vec of preallocated WrappedError/WrappedPanic structs
|
// Vec of preallocated WrappedFailure enums
|
||||||
// Used for callback optimization
|
// Used for callback optimization
|
||||||
prealloc_wrapped_errors: Vec<c_int>,
|
prealloc_wrapped_failures: Vec<c_int>,
|
||||||
|
|
||||||
hook_callback: Option<HookCallback>,
|
hook_callback: Option<HookCallback>,
|
||||||
}
|
}
|
||||||
|
@ -164,7 +164,7 @@ impl Drop for Lua {
|
||||||
unsafe {
|
unsafe {
|
||||||
if !self.ephemeral {
|
if !self.ephemeral {
|
||||||
let extra = &mut *self.extra;
|
let extra = &mut *self.extra;
|
||||||
for index in extra.prealloc_wrapped_errors.clone() {
|
for index in extra.prealloc_wrapped_failures.clone() {
|
||||||
ffi::lua_pushnil(extra.ref_thread);
|
ffi::lua_pushnil(extra.ref_thread);
|
||||||
ffi::lua_replace(extra.ref_thread, index);
|
ffi::lua_replace(extra.ref_thread, index);
|
||||||
extra.ref_free.push(index);
|
extra.ref_free.push(index);
|
||||||
|
@ -446,7 +446,7 @@ impl Lua {
|
||||||
ref_stack_size: ffi::LUA_MINSTACK - 1,
|
ref_stack_size: ffi::LUA_MINSTACK - 1,
|
||||||
ref_stack_top: 0,
|
ref_stack_top: 0,
|
||||||
ref_free: Vec::new(),
|
ref_free: Vec::new(),
|
||||||
prealloc_wrapped_errors: Vec::new(),
|
prealloc_wrapped_failures: Vec::new(),
|
||||||
hook_callback: None,
|
hook_callback: None,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -1558,8 +1558,8 @@ impl Lua {
|
||||||
self.push_ref(&ud.0);
|
self.push_ref(&ud.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value::Error(e) => {
|
Value::Error(err) => {
|
||||||
push_wrapped_error(self.state, e)?;
|
push_gc_userdata(self.state, WrappedFailure::Error(err))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1608,20 +1608,22 @@ impl Lua {
|
||||||
ffi::LUA_TUSERDATA => {
|
ffi::LUA_TUSERDATA => {
|
||||||
// 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.
|
||||||
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
|
||||||
let err = err.clone();
|
Some(WrappedFailure::Error(err)) => {
|
||||||
ffi::lua_pop(state, 1);
|
let err = err.clone();
|
||||||
Value::Error(err)
|
|
||||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
|
||||||
if let Some(panic) = (*panic).0.take() {
|
|
||||||
ffi::lua_pop(state, 1);
|
ffi::lua_pop(state, 1);
|
||||||
resume_unwind(panic);
|
Value::Error(err)
|
||||||
}
|
}
|
||||||
// Previously resumed panic?
|
Some(WrappedFailure::Panic(panic)) => {
|
||||||
ffi::lua_pop(state, 1);
|
if let Some(panic) = panic.take() {
|
||||||
Nil
|
ffi::lua_pop(state, 1);
|
||||||
} else {
|
resume_unwind(panic);
|
||||||
Value::UserData(AnyUserData(self.pop_ref()))
|
}
|
||||||
|
// Previously resumed panic?
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
_ => Value::UserData(AnyUserData(self.pop_ref())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2334,7 +2336,7 @@ impl<'lua, T: AsRef<[u8]> + ?Sized> AsChunk<'lua> for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// An optimized version of `callback_error` that does not allocate `WrappedError+Panic` userdata
|
// An optimized version of `callback_error` that does not allocate `WrappedFailure` userdata
|
||||||
// and instead reuses unsed and cached values from previous calls (or allocates new).
|
// and instead reuses unsed and cached values from previous calls (or allocates new).
|
||||||
// It requires `get_extra` function to return `ExtraData` value.
|
// It requires `get_extra` function to return `ExtraData` value.
|
||||||
unsafe fn callback_error_ext<E, F, R>(state: *mut ffi::lua_State, get_extra: E, f: F) -> R
|
unsafe fn callback_error_ext<E, F, R>(state: *mut ffi::lua_State, get_extra: E, f: F) -> R
|
||||||
|
@ -2357,59 +2359,60 @@ where
|
||||||
cstr!("not enough stack space for callback error handling"),
|
cstr!("not enough stack space for callback error handling"),
|
||||||
);
|
);
|
||||||
|
|
||||||
enum PreallocatedError {
|
enum PreallocatedFailure {
|
||||||
New(*mut c_void),
|
New(*mut WrappedFailure),
|
||||||
Cached(i32),
|
Cached(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory
|
// We cannot shadow Rust errors with Lua ones, so we need to obtain pre-allocated memory
|
||||||
// to store a wrapped error or panic *before* we proceed.
|
// to store a wrapped error or panic *before* we proceed.
|
||||||
let extra = &mut *get_extra(state);
|
let extra = &mut *get_extra(state);
|
||||||
let prealloc_err = {
|
let prealloc_failure = {
|
||||||
match extra.prealloc_wrapped_errors.pop() {
|
match extra.prealloc_wrapped_failures.pop() {
|
||||||
Some(index) => PreallocatedError::Cached(index),
|
Some(index) => PreallocatedFailure::Cached(index),
|
||||||
None => {
|
None => {
|
||||||
let size = mem::size_of::<WrappedError>().max(mem::size_of::<WrappedPanic>());
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedFailure>());
|
||||||
let ud = ffi::lua_newuserdata(state, size);
|
|
||||||
ffi::lua_rotate(state, 1, 1);
|
ffi::lua_rotate(state, 1, 1);
|
||||||
PreallocatedError::New(ud)
|
PreallocatedFailure::New(ud as *mut WrappedFailure)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut get_prealloc_err = || match prealloc_err {
|
let mut get_prealloc_failure = || match prealloc_failure {
|
||||||
PreallocatedError::New(ud) => {
|
PreallocatedFailure::New(ud) => {
|
||||||
ffi::lua_settop(state, 1);
|
ffi::lua_settop(state, 1);
|
||||||
ud
|
ud
|
||||||
}
|
}
|
||||||
PreallocatedError::Cached(index) => {
|
PreallocatedFailure::Cached(index) => {
|
||||||
ffi::lua_settop(state, 0);
|
ffi::lua_settop(state, 0);
|
||||||
ffi::lua_pushvalue(extra.ref_thread, index);
|
ffi::lua_pushvalue(extra.ref_thread, index);
|
||||||
ffi::lua_xmove(extra.ref_thread, state, 1);
|
ffi::lua_xmove(extra.ref_thread, state, 1);
|
||||||
ffi::lua_pushnil(extra.ref_thread);
|
ffi::lua_pushnil(extra.ref_thread);
|
||||||
ffi::lua_replace(extra.ref_thread, index);
|
ffi::lua_replace(extra.ref_thread, index);
|
||||||
extra.ref_free.push(index);
|
extra.ref_free.push(index);
|
||||||
ffi::lua_touserdata(state, -1)
|
ffi::lua_touserdata(state, -1) as *mut WrappedFailure
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||||
Ok(Ok(r)) => {
|
Ok(Ok(r)) => {
|
||||||
// Return unused WrappedError+Panic to the cache
|
// Return unused WrappedFailure to the cache
|
||||||
match prealloc_err {
|
match prealloc_failure {
|
||||||
PreallocatedError::New(_) if extra.prealloc_wrapped_errors.len() < 16 => {
|
PreallocatedFailure::New(_) if extra.prealloc_wrapped_failures.len() < 16 => {
|
||||||
ffi::lua_rotate(state, 1, -1);
|
ffi::lua_rotate(state, 1, -1);
|
||||||
ffi::lua_xmove(state, extra.ref_thread, 1);
|
ffi::lua_xmove(state, extra.ref_thread, 1);
|
||||||
let index = ref_stack_pop(extra);
|
let index = ref_stack_pop(extra);
|
||||||
extra.prealloc_wrapped_errors.push(index);
|
extra.prealloc_wrapped_failures.push(index);
|
||||||
}
|
}
|
||||||
PreallocatedError::New(_) => {
|
PreallocatedFailure::New(_) => {
|
||||||
ffi::lua_remove(state, 1);
|
ffi::lua_remove(state, 1);
|
||||||
}
|
}
|
||||||
PreallocatedError::Cached(index) if extra.prealloc_wrapped_errors.len() < 16 => {
|
PreallocatedFailure::Cached(index)
|
||||||
extra.prealloc_wrapped_errors.push(index);
|
if extra.prealloc_wrapped_failures.len() < 16 =>
|
||||||
|
{
|
||||||
|
extra.prealloc_wrapped_failures.push(index);
|
||||||
}
|
}
|
||||||
PreallocatedError::Cached(index) => {
|
PreallocatedFailure::Cached(index) => {
|
||||||
ffi::lua_pushnil(extra.ref_thread);
|
ffi::lua_pushnil(extra.ref_thread);
|
||||||
ffi::lua_replace(extra.ref_thread, index);
|
ffi::lua_replace(extra.ref_thread, index);
|
||||||
extra.ref_free.push(index);
|
extra.ref_free.push(index);
|
||||||
|
@ -2418,9 +2421,9 @@ where
|
||||||
r
|
r
|
||||||
}
|
}
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
let wrapped_error = get_prealloc_err() as *mut WrappedError;
|
let wrapped_error = get_prealloc_failure();
|
||||||
ptr::write(wrapped_error, WrappedError(err));
|
ptr::write(wrapped_error, WrappedFailure::Error(err));
|
||||||
get_gc_metatable::<WrappedError>(state);
|
get_gc_metatable::<WrappedFailure>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
|
||||||
// Convert to CallbackError and attach traceback
|
// Convert to CallbackError and attach traceback
|
||||||
|
@ -2432,15 +2435,17 @@ where
|
||||||
} else {
|
} else {
|
||||||
"<not enough stack space for traceback>".to_string()
|
"<not enough stack space for traceback>".to_string()
|
||||||
};
|
};
|
||||||
let cause = Arc::new((*wrapped_error).0.clone());
|
if let WrappedFailure::Error(ref mut err) = *wrapped_error {
|
||||||
(*wrapped_error).0 = Error::CallbackError { traceback, cause };
|
let cause = Arc::new(err.clone());
|
||||||
|
*err = Error::CallbackError { traceback, cause };
|
||||||
|
}
|
||||||
|
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
Err(p) => {
|
Err(p) => {
|
||||||
let wrapped_panic = get_prealloc_err() as *mut WrappedPanic;
|
let wrapped_panic = get_prealloc_failure();
|
||||||
ptr::write(wrapped_panic, WrappedPanic(Some(p)));
|
ptr::write(wrapped_panic, WrappedFailure::Panic(Some(p)));
|
||||||
get_gc_metatable::<WrappedPanic>(state);
|
get_gc_metatable::<WrappedFailure>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
|
|
219
src/util.rs
219
src/util.rs
|
@ -167,41 +167,45 @@ 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"
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(err) = get_wrapped_error(state, -1).as_ref() {
|
match get_gc_userdata::<WrappedFailure>(state, -1).as_mut() {
|
||||||
ffi::lua_pop(state, 1);
|
Some(WrappedFailure::Error(err)) => {
|
||||||
err.clone()
|
ffi::lua_pop(state, 1);
|
||||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_mut() {
|
err.clone()
|
||||||
if let Some(p) = (*panic).0.take() {
|
|
||||||
resume_unwind(p);
|
|
||||||
} else {
|
|
||||||
Error::PreviouslyResumedPanic
|
|
||||||
}
|
}
|
||||||
} else {
|
Some(WrappedFailure::Panic(panic)) => {
|
||||||
let err_string = to_string(state, -1);
|
if let Some(p) = panic.take() {
|
||||||
ffi::lua_pop(state, 1);
|
resume_unwind(p);
|
||||||
|
} else {
|
||||||
|
Error::PreviouslyResumedPanic
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let err_string = to_string(state, -1);
|
||||||
|
ffi::lua_pop(state, 1);
|
||||||
|
|
||||||
match err_code {
|
match err_code {
|
||||||
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
ffi::LUA_ERRRUN => Error::RuntimeError(err_string),
|
||||||
ffi::LUA_ERRSYNTAX => {
|
ffi::LUA_ERRSYNTAX => {
|
||||||
Error::SyntaxError {
|
Error::SyntaxError {
|
||||||
// This seems terrible, but as far as I can tell, this is exactly what the
|
// This seems terrible, but as far as I can tell, this is exactly what the
|
||||||
// stock Lua REPL does.
|
// stock Lua REPL does.
|
||||||
incomplete_input: err_string.ends_with("<eof>")
|
incomplete_input: err_string.ends_with("<eof>")
|
||||||
|| err_string.ends_with("'<eof>'"),
|
|| err_string.ends_with("'<eof>'"),
|
||||||
message: err_string,
|
message: err_string,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
ffi::LUA_ERRERR => {
|
||||||
|
// This error is raised when the error handler raises an error too many times
|
||||||
|
// recursively, and continuing to trigger the error handler would cause a stack
|
||||||
|
// overflow. It is not very useful to differentiate between this and "ordinary"
|
||||||
|
// runtime errors, so we handle them the same way.
|
||||||
|
Error::RuntimeError(err_string)
|
||||||
|
}
|
||||||
|
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
||||||
|
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
||||||
|
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
||||||
|
_ => mlua_panic!("unrecognized lua error code"),
|
||||||
}
|
}
|
||||||
ffi::LUA_ERRERR => {
|
|
||||||
// This error is raised when the error handler raises an error too many times
|
|
||||||
// recursively, and continuing to trigger the error handler would cause a stack
|
|
||||||
// overflow. It is not very useful to differentiate between this and "ordinary"
|
|
||||||
// runtime errors, so we handle them the same way.
|
|
||||||
Error::RuntimeError(err_string)
|
|
||||||
}
|
|
||||||
ffi::LUA_ERRMEM => Error::MemoryError(err_string),
|
|
||||||
#[cfg(any(feature = "lua53", feature = "lua52"))]
|
|
||||||
ffi::LUA_ERRGCMM => Error::GarbageCollectorError(err_string),
|
|
||||||
_ => mlua_panic!("unrecognized lua error code"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -480,10 +484,7 @@ where
|
||||||
|
|
||||||
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
|
// We cannot shadow Rust errors with Lua ones, we pre-allocate enough memory
|
||||||
// to store a wrapped error or panic *before* we proceed.
|
// to store a wrapped error or panic *before* we proceed.
|
||||||
let ud = ffi::lua_newuserdata(
|
let ud = ffi::lua_newuserdata(state, mem::size_of::<WrappedFailure>());
|
||||||
state,
|
|
||||||
mem::size_of::<WrappedError>().max(mem::size_of::<WrappedPanic>()),
|
|
||||||
);
|
|
||||||
ffi::lua_rotate(state, 1, 1);
|
ffi::lua_rotate(state, 1, 1);
|
||||||
|
|
||||||
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
match catch_unwind(AssertUnwindSafe(|| f(nargs))) {
|
||||||
|
@ -494,9 +495,9 @@ where
|
||||||
Ok(Err(err)) => {
|
Ok(Err(err)) => {
|
||||||
ffi::lua_settop(state, 1);
|
ffi::lua_settop(state, 1);
|
||||||
|
|
||||||
let wrapped_error = ud as *mut WrappedError;
|
let wrapped_error = ud as *mut WrappedFailure;
|
||||||
ptr::write(wrapped_error, WrappedError(err));
|
ptr::write(wrapped_error, WrappedFailure::Error(err));
|
||||||
get_gc_metatable::<WrappedError>(state);
|
get_gc_metatable::<WrappedFailure>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
|
|
||||||
// Convert to CallbackError and attach traceback
|
// Convert to CallbackError and attach traceback
|
||||||
|
@ -508,15 +509,17 @@ where
|
||||||
} else {
|
} else {
|
||||||
"<not enough stack space for traceback>".to_string()
|
"<not enough stack space for traceback>".to_string()
|
||||||
};
|
};
|
||||||
let cause = Arc::new((*wrapped_error).0.clone());
|
if let WrappedFailure::Error(ref mut err) = *wrapped_error {
|
||||||
(*wrapped_error).0 = Error::CallbackError { traceback, cause };
|
let cause = Arc::new(err.clone());
|
||||||
|
*err = Error::CallbackError { traceback, cause };
|
||||||
|
}
|
||||||
|
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
Err(p) => {
|
Err(p) => {
|
||||||
ffi::lua_settop(state, 1);
|
ffi::lua_settop(state, 1);
|
||||||
ptr::write(ud as *mut WrappedPanic, WrappedPanic(Some(p)));
|
ptr::write(ud as *mut WrappedFailure, WrappedFailure::Panic(Some(p)));
|
||||||
get_gc_metatable::<WrappedPanic>(state);
|
get_gc_metatable::<WrappedFailure>(state);
|
||||||
ffi::lua_setmetatable(state, -2);
|
ffi::lua_setmetatable(state, -2);
|
||||||
ffi::lua_error(state)
|
ffi::lua_error(state)
|
||||||
}
|
}
|
||||||
|
@ -530,9 +533,7 @@ pub unsafe extern "C" fn error_traceback(state: *mut ffi::lua_State) -> c_int {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if get_gc_userdata::<WrappedError>(state, -1).is_null()
|
if get_gc_userdata::<WrappedFailure>(state, -1).is_null() {
|
||||||
&& get_gc_userdata::<WrappedPanic>(state, -1).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, 1);
|
ffi::luaL_traceback(state, state, s, 1);
|
||||||
|
@ -558,7 +559,9 @@ pub unsafe extern "C" fn safe_pcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_insert(state, 1);
|
ffi::lua_insert(state, 1);
|
||||||
ffi::lua_gettop(state)
|
ffi::lua_gettop(state)
|
||||||
} else {
|
} else {
|
||||||
if !get_gc_userdata::<WrappedPanic>(state, -1).is_null() {
|
if let Some(WrappedFailure::Panic(_)) =
|
||||||
|
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||||
|
{
|
||||||
ffi::lua_error(state);
|
ffi::lua_error(state);
|
||||||
}
|
}
|
||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
|
@ -572,7 +575,9 @@ 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 {
|
||||||
ffi::luaL_checkstack(state, 2, ptr::null());
|
ffi::luaL_checkstack(state, 2, ptr::null());
|
||||||
|
|
||||||
if !get_gc_userdata::<WrappedPanic>(state, -1).is_null() {
|
if let Some(WrappedFailure::Panic(_)) =
|
||||||
|
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||||
|
{
|
||||||
1
|
1
|
||||||
} else {
|
} else {
|
||||||
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
ffi::lua_pushvalue(state, ffi::lua_upvalueindex(1));
|
||||||
|
@ -600,7 +605,9 @@ pub unsafe extern "C" fn safe_xpcall(state: *mut ffi::lua_State) -> c_int {
|
||||||
ffi::lua_insert(state, 2);
|
ffi::lua_insert(state, 2);
|
||||||
ffi::lua_gettop(state) - 1
|
ffi::lua_gettop(state) - 1
|
||||||
} else {
|
} else {
|
||||||
if !get_gc_userdata::<WrappedPanic>(state, -1).is_null() {
|
if let Some(WrappedFailure::Panic(_)) =
|
||||||
|
get_gc_userdata::<WrappedFailure>(state, -1).as_ref()
|
||||||
|
{
|
||||||
ffi::lua_error(state);
|
ffi::lua_error(state);
|
||||||
}
|
}
|
||||||
ffi::lua_pushboolean(state, 0);
|
ffi::lua_pushboolean(state, 0);
|
||||||
|
@ -632,23 +639,6 @@ pub unsafe fn get_main_state(state: *mut ffi::lua_State) -> Option<*mut ffi::lua
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pushes a WrappedError to the top of the stack.
|
|
||||||
// Uses 3 stack spaces and does not call checkstack.
|
|
||||||
pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: Error) -> Result<()> {
|
|
||||||
push_gc_userdata::<WrappedError>(state, WrappedError(err))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if the value at the given index is a WrappedError, and if it is returns a pointer to it,
|
|
||||||
// otherwise returns null.
|
|
||||||
// Uses 2 stack spaces and does not call checkstack.
|
|
||||||
pub unsafe fn get_wrapped_error(state: *mut ffi::lua_State, index: c_int) -> *const Error {
|
|
||||||
let ud = get_gc_userdata::<WrappedError>(state, index);
|
|
||||||
if ud.is_null() {
|
|
||||||
return ptr::null();
|
|
||||||
}
|
|
||||||
&(*ud).0
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the internal (with __gc method) metatable for a type T.
|
// Initialize the internal (with __gc method) metatable for a type T.
|
||||||
// Uses 6 stack spaces and calls checkstack.
|
// Uses 6 stack spaces and calls checkstack.
|
||||||
pub unsafe fn init_gc_metatable<T: Any>(
|
pub unsafe fn init_gc_metatable<T: Any>(
|
||||||
|
@ -697,7 +687,6 @@ pub unsafe fn get_gc_metatable<T: Any>(state: *mut ffi::lua_State) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the error, panic, and destructed userdata metatables.
|
// Initialize the error, panic, and destructed userdata metatables.
|
||||||
// Returns address of WrappedError and WrappedPanic metatables in Lua registry.
|
|
||||||
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||||
check_stack(state, 7)?;
|
check_stack(state, 7)?;
|
||||||
|
|
||||||
|
@ -707,63 +696,67 @@ 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 = if let Some(error) = get_wrapped_error(state, -1).as_ref() {
|
let err_buf = match get_gc_userdata::<WrappedFailure>(state, -1).as_ref() {
|
||||||
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
Some(WrappedFailure::Error(error)) => {
|
||||||
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
let err_buf_key = &ERROR_PRINT_BUFFER_KEY as *const u8 as *const c_void;
|
||||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
ffi::lua_rawgetp(state, ffi::LUA_REGISTRYINDEX, err_buf_key);
|
||||||
ffi::lua_pop(state, 2);
|
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||||
|
ffi::lua_pop(state, 2);
|
||||||
|
|
||||||
(*err_buf).clear();
|
(*err_buf).clear();
|
||||||
// Depending on how the API is used and what error types scripts are given, it may
|
// Depending on how the API is used and what error types scripts are given, it may
|
||||||
// be possible to make this consume arbitrary amounts of memory (for example, some
|
// be possible to make this consume arbitrary amounts of memory (for example, some
|
||||||
// kind of recursive error structure?)
|
// kind of recursive error structure?)
|
||||||
let _ = write!(&mut (*err_buf), "{}", error);
|
let _ = write!(&mut (*err_buf), "{}", error);
|
||||||
// Find first two sources that caused the error
|
// Find first two sources that caused the error
|
||||||
let mut source1 = error.source();
|
let mut source1 = error.source();
|
||||||
let mut source0 = source1.and_then(|s| s.source());
|
let mut source0 = source1.and_then(|s| s.source());
|
||||||
while let Some(source) = source0.and_then(|s| s.source()) {
|
while let Some(source) = source0.and_then(|s| s.source()) {
|
||||||
source1 = source0;
|
source1 = source0;
|
||||||
source0 = Some(source);
|
source0 = Some(source);
|
||||||
}
|
|
||||||
match (source1, source0) {
|
|
||||||
(_, Some(error0)) if error0.to_string().contains("\nstack traceback:\n") => {
|
|
||||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
|
||||||
}
|
}
|
||||||
(Some(error1), Some(error0)) => {
|
match (source1, source0) {
|
||||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
(_, Some(error0))
|
||||||
let s = error1.to_string();
|
if error0.to_string().contains("\nstack traceback:\n") =>
|
||||||
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
{
|
||||||
let _ = write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||||
}
|
}
|
||||||
|
(Some(error1), Some(error0)) => {
|
||||||
|
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error0);
|
||||||
|
let s = error1.to_string();
|
||||||
|
if let Some(traceback) = s.splitn(2, "\nstack traceback:\n").nth(1) {
|
||||||
|
let _ =
|
||||||
|
write!(&mut (*err_buf), "\nstack traceback:\n{}", traceback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Some(error1), None) => {
|
||||||
|
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
(Some(error1), None) => {
|
Ok(err_buf)
|
||||||
let _ = write!(&mut (*err_buf), "\ncaused by: {}", error1);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
Ok(err_buf)
|
Some(WrappedFailure::Panic(Some(ref panic))) => {
|
||||||
} else if let Some(panic) = get_gc_userdata::<WrappedPanic>(state, -1).as_ref() {
|
|
||||||
if let Some(ref p) = (*panic).0 {
|
|
||||||
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);
|
||||||
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
let err_buf = ffi::lua_touserdata(state, -1) as *mut String;
|
||||||
(*err_buf).clear();
|
(*err_buf).clear();
|
||||||
ffi::lua_pop(state, 2);
|
ffi::lua_pop(state, 2);
|
||||||
|
|
||||||
if let Some(msg) = p.downcast_ref::<&str>() {
|
if let Some(msg) = panic.downcast_ref::<&str>() {
|
||||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||||
} else if let Some(msg) = p.downcast_ref::<String>() {
|
} else if let Some(msg) = panic.downcast_ref::<String>() {
|
||||||
let _ = write!(&mut (*err_buf), "{}", msg);
|
let _ = write!(&mut (*err_buf), "{}", msg);
|
||||||
} else {
|
} else {
|
||||||
let _ = write!(&mut (*err_buf), "<panic>");
|
let _ = write!(&mut (*err_buf), "<panic>");
|
||||||
};
|
};
|
||||||
Ok(err_buf)
|
Ok(err_buf)
|
||||||
} else {
|
|
||||||
Err(Error::PreviouslyResumedPanic)
|
|
||||||
}
|
}
|
||||||
} else {
|
Some(WrappedFailure::Panic(None)) => Err(Error::PreviouslyResumedPanic),
|
||||||
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
_ => {
|
||||||
Err(Error::UserDataTypeMismatch)
|
// I'm not sure whether this is possible to trigger without bugs in mlua?
|
||||||
|
Err(Error::UserDataTypeMismatch)
|
||||||
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
push_string(state, &*err_buf)?;
|
push_string(state, &*err_buf)?;
|
||||||
|
@ -773,15 +766,7 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
init_gc_metatable::<WrappedError>(
|
init_gc_metatable::<WrappedFailure>(
|
||||||
state,
|
|
||||||
Some(|state| {
|
|
||||||
ffi::lua_pushcfunction(state, error_tostring);
|
|
||||||
rawset_field(state, -2, "__tostring")
|
|
||||||
}),
|
|
||||||
)?;
|
|
||||||
|
|
||||||
init_gc_metatable::<WrappedPanic>(
|
|
||||||
state,
|
state,
|
||||||
Some(|state| {
|
Some(|state| {
|
||||||
ffi::lua_pushcfunction(state, error_tostring);
|
ffi::lua_pushcfunction(state, error_tostring);
|
||||||
|
@ -856,8 +841,10 @@ pub unsafe fn init_error_registry(state: *mut ffi::lua_State) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct WrappedError(pub Error);
|
pub(crate) enum WrappedFailure {
|
||||||
pub(crate) struct WrappedPanic(pub Option<Box<dyn Any + Send + 'static>>);
|
Error(Error),
|
||||||
|
Panic(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