diff options
author | kyren <kerriganw@gmail.com> | 2017-06-24 20:57:04 -0400 |
---|---|---|
committer | kyren <kerriganw@gmail.com> | 2017-06-24 20:57:04 -0400 |
commit | 3deb6df525369f8b298b8ce0649f7b1dce6d13df (patch) | |
tree | dd6571235a505f9f73479548fb169612be0d3862 /src | |
parent | 47db72cac47bf6fc3d09fa0747997a102d41df9c (diff) | |
download | mlua-3deb6df525369f8b298b8ce0649f7b1dce6d13df.zip |
Lots of LuaError changes
It is possible that I have gone too far here into error discrimination and
should scale it back, not sure yet.
Diffstat (limited to 'src')
-rw-r--r-- | src/conversion.rs | 65 | ||||
-rw-r--r-- | src/error.rs | 166 | ||||
-rw-r--r-- | src/lua.rs | 70 | ||||
-rw-r--r-- | src/tests.rs | 16 | ||||
-rw-r--r-- | src/util.rs | 146 |
5 files changed, 308 insertions, 155 deletions
diff --git a/src/conversion.rs b/src/conversion.rs index 42b3bfa..98a68d4 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -38,9 +38,9 @@ impl<'lua> FromLua<'lua> for LuaTable<'lua> { fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<LuaTable<'lua>> { match value { LuaValue::Table(table) => Ok(table), - _ => Err(LuaError::ConversionError( - "cannot convert lua value to table".to_owned(), - )), + _ => Err( + LuaConversionError::FromLua("cannot convert lua value to table".to_owned()).into(), + ), } } } @@ -55,9 +55,10 @@ impl<'lua> FromLua<'lua> for LuaFunction<'lua> { fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<LuaFunction<'lua>> { match value { LuaValue::Function(table) => Ok(table), - _ => Err(LuaError::ConversionError( - "cannot convert lua value to function".to_owned(), - )), + _ => Err( + LuaConversionError::FromLua("cannot convert lua value to function".to_owned()) + .into(), + ), } } } @@ -72,9 +73,10 @@ impl<'lua> FromLua<'lua> for LuaUserData<'lua> { fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<LuaUserData<'lua>> { match value { LuaValue::UserData(ud) => Ok(ud), - _ => Err(LuaError::ConversionError( - "cannot convert lua value to userdata".to_owned(), - )), + _ => Err( + LuaConversionError::FromLua("cannot convert lua value to userdata".to_owned()) + .into(), + ), } } } @@ -89,9 +91,10 @@ impl<'lua> FromLua<'lua> for LuaThread<'lua> { fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<LuaThread<'lua>> { match value { LuaValue::Thread(t) => Ok(t), - _ => Err(LuaError::ConversionError( - "cannot convert lua value to thread".to_owned(), - )), + _ => Err( + LuaConversionError::FromLua("cannot convert lua value to thread".to_owned()) + .into(), + ), } } } @@ -106,9 +109,10 @@ impl<'lua, T: LuaUserDataType + Copy> FromLua<'lua> for T { fn from_lua(value: LuaValue<'lua>, _: &'lua Lua) -> LuaResult<T> { match value { LuaValue::UserData(ud) => Ok(*ud.borrow::<T>()?), - _ => Err(LuaError::ConversionError( - "cannot convert lua value to userdata".to_owned(), - )), + _ => Err( + LuaConversionError::FromLua("cannot convert lua value to userdata".to_owned()) + .into(), + ), } } } @@ -139,9 +143,11 @@ impl<'lua> FromLua<'lua> for LightUserData { fn from_lua(v: LuaValue, _: &'lua Lua) -> LuaResult<Self> { match v { LuaValue::LightUserData(ud) => Ok(ud), - _ => Err(LuaError::ConversionError( - "cannot convert lua value to lightuserdata".to_owned(), - )), + _ => Err( + LuaConversionError::FromLua( + "cannot convert lua value to lightuserdata".to_owned(), + ).into(), + ), } } } @@ -221,9 +227,10 @@ impl<'lua, T: FromLua<'lua>> FromLua<'lua> for Vec<T> { if let LuaValue::Table(table) = value { table.sequence_values().collect() } else { - Err(LuaError::ConversionError( - "cannot convert lua value to table for Vec".to_owned(), - )) + Err( + LuaConversionError::FromLua("cannot convert lua value to table for Vec".to_owned()) + .into(), + ) } } } @@ -239,9 +246,11 @@ impl<'lua, K: Eq + Hash + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for Has if let LuaValue::Table(table) = value { table.pairs().collect() } else { - Err(LuaError::ConversionError( - "cannot convert lua value to table for HashMap".to_owned(), - )) + Err( + LuaConversionError::FromLua( + "cannot convert lua value to table for HashMap".to_owned(), + ).into(), + ) } } } @@ -257,9 +266,11 @@ impl<'lua, K: Ord + FromLua<'lua>, V: FromLua<'lua>> FromLua<'lua> for BTreeMap< if let LuaValue::Table(table) = value { table.pairs().collect() } else { - Err(LuaError::ConversionError( - "cannot convert lua value to table for BTreeMap".to_owned(), - )) + Err( + LuaConversionError::FromLua( + "cannot convert lua value to table for BTreeMap".to_owned(), + ).into(), + ) } } } diff --git a/src/error.rs b/src/error.rs index df5b590..a1a651c 100644 --- a/src/error.rs +++ b/src/error.rs @@ -6,17 +6,61 @@ use std::ffi::NulError; use std::str::Utf8Error; #[derive(Debug, Clone)] -pub enum LuaError { - ScriptError(String), - CallbackError(String, Arc<LuaError>), +pub enum LuaSyntaxError { + /// A generic syntax error + Syntax(String), + /// A syntax error due to an incomplete statement, useful for implementing a + /// Lua REPL. IncompleteStatement(String), - CoroutineInactive, - StackOverflow, - UserDataBorrowError, - UserDataBorrowMutError, +} + +#[derive(Debug, Clone)] +pub enum LuaConversionError { + /// A generic Rust -> Lua type conversion error. + ToLua(String), + /// A generic Lua -> Rust type conversion error. + FromLua(String), + /// A Lua string was not valid utf8 on conversion to a rust String. Utf8Error(Utf8Error), + /// A rust String contained a NUL character, and thus was not convertible to + /// a Lua string. NulError(NulError), - ConversionError(String), +} + +#[derive(Debug, Clone)] +pub enum LuaUserDataError { + /// A `LuaUserData` borrow failed because the expected type was not the + /// contained type. + TypeMismatch, + /// A `LuaUserData` immutable borrow failed because it was already borrowed + /// mutably. + BorrowError, + /// A `LuaUserData` mutable borrow failed because it was already borrowed. + BorrowMutError, +} + +#[derive(Debug, Clone)] +pub enum LuaError { + /// Lua syntax error, aka LUA_ERRSYNTAX. + SyntaxError(LuaSyntaxError), + /// Lua runtime error, aka LUA_ERRRUN. + RuntimeError(String), + /// Lua error from inside an error handler, aka LUA_ERRERR. + ErrorError(String), + /// An error resulting from a Lua <-> Rust type conversion + ConversionError(LuaConversionError), + /// Insufficient Lua stack space. + StackOverflow, + /// A `LuaThread` was resumed and the coroutine was no longer active. + CoroutineInactive, + /// A `LuaUserData` borrow of the internal value failed. + UserDataError(LuaUserDataError), + /// Lua error that originated as a LuaError in a callback. The first field + /// is the lua error as a string, the second field is the Arc holding the + /// original LuaError. + CallbackError(String, Arc<LuaError>), + /// Any custom external error type, mostly useful for returning external + /// error types from callbacks. ExternalError(Arc<Error + Send + Sync>), } @@ -25,25 +69,46 @@ pub type LuaResult<T> = Result<T, LuaError>; impl fmt::Display for LuaError { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match self { - &LuaError::ScriptError(ref msg) => write!(fmt, "Error executing lua script: {}", msg), - &LuaError::CallbackError(ref msg, _) => { - write!(fmt, "Error during lua callback: {}", msg) + &LuaError::SyntaxError(LuaSyntaxError::Syntax(ref msg)) => { + write!(fmt, "Lua syntax error: {}", msg) } - &LuaError::ExternalError(ref err) => err.fmt(fmt), - &LuaError::IncompleteStatement(ref msg) => { - write!(fmt, "Incomplete lua statement: {}", msg) + &LuaError::SyntaxError(LuaSyntaxError::IncompleteStatement(ref msg)) => { + write!(fmt, "Lua syntax error: {}", msg) + } + + &LuaError::RuntimeError(ref msg) => write!(fmt, "Lua runtime error: {}", msg), + &LuaError::ErrorError(ref msg) => write!(fmt, "Lua error in error handler: {}", msg), + + &LuaError::ConversionError(LuaConversionError::ToLua(ref msg)) => { + write!(fmt, "Error converting rust type to lua: {}", msg) + } + &LuaError::ConversionError(LuaConversionError::FromLua(ref msg)) => { + write!(fmt, "Error converting lua type to rust: {}", msg) + } + &LuaError::ConversionError(LuaConversionError::Utf8Error(ref msg)) => { + write!(fmt, "Error converting lua string to rust: {}", msg) + } + &LuaError::ConversionError(LuaConversionError::NulError(ref msg)) => { + write!(fmt, "Error converting rust string to lua: {}", msg) } + + &LuaError::StackOverflow => write!(fmt, "Lua out of stack space"), &LuaError::CoroutineInactive => write!(fmt, "Cannot resume inactive coroutine"), - &LuaError::ConversionError(ref msg) => { - write!(fmt, "Error converting lua type: {}", msg) + + &LuaError::UserDataError(LuaUserDataError::TypeMismatch) => { + write!(fmt, "Userdata not expected type") + } + &LuaError::UserDataError(LuaUserDataError::BorrowError) => { + write!(fmt, "Userdata already mutably borrowed") } - &LuaError::StackOverflow => write!(fmt, "Lua stack overflow"), - &LuaError::UserDataBorrowError => write!(fmt, "Userdata already mutably borrowed"), - &LuaError::UserDataBorrowMutError => write!(fmt, "Userdata already borrowed"), - &LuaError::Utf8Error(ref err) => write!(fmt, "Lua string utf8 error: {}", err), - &LuaError::NulError(ref err) => { - write!(fmt, "String passed to lua contains null: {}", err) + &LuaError::UserDataError(LuaUserDataError::BorrowMutError) => { + write!(fmt, "Userdata already borrowed") } + + &LuaError::CallbackError(ref msg, _) => { + write!(fmt, "Error during lua callback: {}", msg) + } + &LuaError::ExternalError(ref err) => err.fmt(fmt), } } } @@ -51,17 +116,38 @@ impl fmt::Display for LuaError { impl Error for LuaError { fn description(&self) -> &str { match self { - &LuaError::ScriptError(_) => "lua script error", + &LuaError::SyntaxError(LuaSyntaxError::Syntax(_)) => "lua syntax error", + &LuaError::SyntaxError(LuaSyntaxError::IncompleteStatement(_)) => { + "lua incomplete statement" + } + + &LuaError::RuntimeError(_) => "lua runtime error", + &LuaError::ErrorError(_) => "lua error handling error", + + &LuaError::ConversionError(LuaConversionError::ToLua(_)) => "conversion error to lua", + &LuaError::ConversionError(LuaConversionError::FromLua(_)) => { + "conversion error from lua" + } + &LuaError::ConversionError(LuaConversionError::Utf8Error(_)) => { + "lua string utf8 conversion error" + } + &LuaError::ConversionError(LuaConversionError::NulError(_)) => "string contains null", + + &LuaError::StackOverflow => "lua stack overflow", + &LuaError::CoroutineInactive => "lua coroutine inactive", + + &LuaError::UserDataError(LuaUserDataError::TypeMismatch) => { + "lua userdata type mismatch" + } + &LuaError::UserDataError(LuaUserDataError::BorrowError) => { + "lua userdata already borrowed" + } + &LuaError::UserDataError(LuaUserDataError::BorrowMutError) => { + "lua userdata already mutably borrowed" + } + &LuaError::CallbackError(_, _) => "lua callback error", &LuaError::ExternalError(ref err) => err.description(), - &LuaError::IncompleteStatement(_) => "lua incomplete statement", - &LuaError::CoroutineInactive => "lua coroutine inactive", - &LuaError::ConversionError(_) => "lua conversion error", - &LuaError::StackOverflow => "lua stack overflow", - &LuaError::UserDataBorrowError => "lua userdata already mutably borrowed", - &LuaError::UserDataBorrowMutError => "lua userdata already borrowed", - &LuaError::Utf8Error(_) => "lua string utf8 conversion error", - &LuaError::NulError(_) => "string null error", } } @@ -120,3 +206,21 @@ where self.map_err(|e| e.to_lua_err()) } } + +impl From<LuaSyntaxError> for LuaError { + fn from(err: LuaSyntaxError) -> LuaError { + LuaError::SyntaxError(err) + } +} + +impl From<LuaConversionError> for LuaError { + fn from(err: LuaConversionError) -> LuaError { + LuaError::ConversionError(err) + } +} + +impl From<LuaUserDataError> for LuaError { + fn from(err: LuaUserDataError) -> LuaError { + LuaError::UserDataError(err) + } +} @@ -125,7 +125,7 @@ pub trait FromLuaMulti<'a>: Sized { impl<'lua> ToLua<'lua> for LuaError { fn to_lua(self, lua: &'lua Lua) -> LuaResult<LuaValue<'lua>> { unsafe { - push_error(lua.state, self); + push_wrapped_error(lua.state, self); Ok(lua.pop_value(lua.state)) } } @@ -207,7 +207,7 @@ impl<'lua> LuaString<'lua> { assert_eq!(ffi::lua_type(lua.state, -1), ffi::LUA_TSTRING); let s = CStr::from_ptr(ffi::lua_tostring(lua.state, -1)) .to_str() - .map_err(|e| LuaError::Utf8Error(e))?; + .map_err(|e| LuaConversionError::Utf8Error(e))?; ffi::lua_pop(lua.state, 1); Ok(s) }) @@ -855,10 +855,11 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> { let userdata = userdata.borrow::<T>()?; method(lua, &userdata, args) } else { - Err(LuaError::ConversionError( - "No userdata supplied as first argument to method" - .to_owned(), - )) + Err( + LuaConversionError::FromLua( + "No userdata supplied as first argument to method".to_owned(), + ).into(), + ) }) } @@ -872,10 +873,11 @@ impl<T: LuaUserDataType> LuaUserDataMethods<T> { let mut userdata = userdata.borrow_mut::<T>()?; method(lua, &mut userdata, args) } else { - Err(LuaError::ConversionError( - "No userdata supplied as first argument to method" - .to_owned(), - )) + Err( + LuaConversionError::FromLua( + "No userdata supplied as first argument to method".to_owned(), + ).into(), + ) }) } @@ -903,7 +905,7 @@ impl<'lua> LuaUserData<'lua> { pub fn borrow<T: LuaUserDataType>(&self) -> LuaResult<Ref<T>> { self.inspect(|cell| { Ok( - cell.try_borrow().map_err(|_| LuaError::UserDataBorrowError)?, + cell.try_borrow().map_err(|_| LuaUserDataError::BorrowError)?, ) }) } @@ -912,7 +914,7 @@ impl<'lua> LuaUserData<'lua> { pub fn borrow_mut<T: LuaUserDataType>(&self) -> LuaResult<RefMut<T>> { self.inspect(|cell| { Ok(cell.try_borrow_mut().map_err( - |_| LuaError::UserDataBorrowMutError, + |_| LuaUserDataError::BorrowError, )?) }) } @@ -929,14 +931,10 @@ impl<'lua> LuaUserData<'lua> { lua.push_ref(lua.state, &self.0); let userdata = ffi::lua_touserdata(lua.state, -1); - if userdata.is_null() { - return Err(LuaError::ConversionError("value not userdata".to_owned())); - } + assert!(!userdata.is_null()); if ffi::lua_getmetatable(lua.state, -1) == 0 { - return Err(LuaError::ConversionError( - "value has no metatable".to_owned(), - )); + return Err(LuaUserDataError::TypeMismatch.into()); } ffi::lua_rawgeti( @@ -945,9 +943,7 @@ impl<'lua> LuaUserData<'lua> { lua.userdata_metatable::<T>()? as ffi::lua_Integer, ); if ffi::lua_rawequal(lua.state, -1, -2) == 0 { - return Err(LuaError::ConversionError( - "wrong metatable type for lua userdata".to_owned(), - )); + return Err(LuaUserDataError::TypeMismatch.into()); } let res = func(&*(userdata as *const RefCell<T>)); @@ -1064,9 +1060,9 @@ impl Lua { handle_error( self.state, if let Some(name) = name { - let name = CString::new(name.to_owned()).map_err( - |e| LuaError::NulError(e), - )?; + let name = CString::new(name.to_owned()).map_err(|e| { + LuaConversionError::NulError(e) + })?; ffi::luaL_loadbuffer( self.state, source.as_ptr() as *const c_char, @@ -1260,9 +1256,11 @@ impl Lua { check_stack(self.state, 1)?; self.push_value(self.state, v); if ffi::lua_tostring(self.state, -1).is_null() { - Err(LuaError::ConversionError( - "cannot convert lua value to string".to_owned(), - )) + Err( + LuaConversionError::FromLua( + "cannot convert lua value to string".to_owned(), + ).into(), + ) } else { Ok(LuaString(self.pop_ref(self.state))) } @@ -1285,9 +1283,11 @@ impl Lua { let mut isint = 0; let i = ffi::lua_tointegerx(self.state, -1, &mut isint); if isint == 0 { - Err(LuaError::ConversionError( - "cannot convert lua value to integer".to_owned(), - )) + Err( + LuaConversionError::FromLua( + "cannot convert lua value to integer".to_owned(), + ).into(), + ) } else { ffi::lua_pop(self.state, 1); Ok(i) @@ -1311,9 +1311,11 @@ impl Lua { let mut isnum = 0; let n = ffi::lua_tonumberx(self.state, -1, &mut isnum); if isnum == 0 { - Err(LuaError::ConversionError( - "cannot convert lua value to number".to_owned(), - )) + Err( + LuaConversionError::FromLua( + "cannot convert lua value to number".to_owned(), + ).into(), + ) } else { ffi::lua_pop(self.state, 1); Ok(n) @@ -1485,7 +1487,7 @@ impl Lua { ffi::LUA_TTHREAD => LuaValue::Thread(LuaThread(self.pop_ref(state))), - _ => unreachable!("LUA_TNONE in pop_value"), + _ => panic!("LUA_TNONE in pop_value"), } } diff --git a/src/tests.rs b/src/tests.rs index 7c42ac0..42ed823 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -68,7 +68,7 @@ fn test_eval() { assert_eq!(lua.eval::<bool>("false == false").unwrap(), true); assert_eq!(lua.eval::<i32>("return 1 + 2").unwrap(), 3); match lua.eval::<()>("if true then") { - Err(LuaError::IncompleteStatement(_)) => {} + Err(LuaError::SyntaxError(LuaSyntaxError::IncompleteStatement(_))) => {} r => panic!("expected IncompleteStatement, got {:?}", r), } } @@ -497,8 +497,8 @@ fn test_error() { assert!(no_error.call::<_, ()>(()).is_ok()); match lua_error.call::<_, ()>(()) { - Err(LuaError::ScriptError(_)) => {} - Err(_) => panic!("error is not ScriptError kind"), + Err(LuaError::RuntimeError(_)) => {} + Err(_) => panic!("error is not RuntimeError kind"), _ => panic!("error not returned"), } match rust_error.call::<_, ()>(()) { @@ -506,6 +506,16 @@ fn test_error() { Err(_) => panic!("error is not CallbackError kind"), _ => panic!("error not returned"), } + match lua.eval::<()>("if youre happy and you know it syntax error") { + Err(LuaError::SyntaxError(LuaSyntaxError::Syntax(_))) => {} + Err(_) => panic!("error is not LuaSyntaxError::Syntax kind"), + _ => panic!("error not returned"), + } + match lua.eval::<()>("function i_will_finish_what_i()") { + Err(LuaError::SyntaxError(LuaSyntaxError::IncompleteStatement(_))) => {} + Err(_) => panic!("error is not LuaSyntaxError::IncompleteStatement kind"), + _ => panic!("error not returned"), + } test_pcall.call::<_, ()>(()).unwrap(); diff --git a/src/util.rs b/src/util.rs index 5ee72b1..57d9e72 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,5 +1,6 @@ use std::mem; use std::ptr; +use std::process; use std::sync::Arc; use std::ffi::CStr; use std::any::Any; @@ -7,7 +8,7 @@ use std::os::raw::{c_char, c_int, c_void}; use std::panic::{catch_unwind, resume_unwind, UnwindSafe}; use ffi; -use error::{LuaResult, LuaError}; +use error::{LuaResult, LuaSyntaxError, LuaError}; macro_rules! cstr { ($s:expr) => ( @@ -100,11 +101,7 @@ where ffi::lua_insert(state, -(nargs + 1)); ffi::lua_pushlightuserdata(state, &mut func as *mut F as *mut c_void); mem::forget(func); - if pcall_with_traceback(state, nargs + 1, nresults) != ffi::LUA_OK { - Err(pop_error(state)) - } else { - Ok(()) - } + handle_error(state, pcall_with_traceback(state, nargs + 1, nresults)) } let mut res = None; @@ -117,12 +114,59 @@ where // If the return code indicates an error, pops the error off of the stack and // returns Err. If the error was actually a rust panic, clears the current lua -// stack and panics. -pub unsafe fn handle_error(state: *mut ffi::lua_State, ret: c_int) -> LuaResult<()> { - if ret != ffi::LUA_OK && ret != ffi::LUA_YIELD { - Err(pop_error(state)) - } else { +// stack and panics. If the error on the top of the stack is actually a +// WrappedError::Error, just returns it. If the error on the top of the stack +// is a WrappedError::Panic, clears the rust stack and resumes the panic, +// otherwise interprets the error as the appropriate lua error. +pub unsafe fn handle_error(state: *mut ffi::lua_State, err: c_int) -> LuaResult<()> { + if err == ffi::LUA_OK || err == ffi::LUA_YIELD { Ok(()) + } else { + if is_wrapped_error(state, -1) { + Err(pop_wrapped_error(state)) + } else { + let err_string = + if let Some(s) = ffi::lua_tolstring(state, -1, ptr::null_mut()).as_ref() { + CStr::from_ptr(s).to_str().unwrap().to_owned() + } else { + "<unprintable error>".to_owned() + }; + ffi::lua_pop(state, 1); + + Err(match err { + ffi::LUA_ERRRUN => LuaError::RuntimeError(err_string), + ffi::LUA_ERRSYNTAX => { + // This seems terrible, but as far as I can tell, this is exactly what the stock lua + // repl does. + if err_string.ends_with("<eof>") { + LuaSyntaxError::IncompleteStatement(err_string).into() + } else { + LuaSyntaxError::Syntax(err_string).into() + } + } + ffi::LUA_ERRERR => LuaError::ErrorError(err_string), + ffi::LUA_ERRMEM => { + // This is not impossible to hit, but this library is not set up + // to handle this properly. Lua does a longjmp on out of memory + // (like all lua errors), but it can do this from a huge number + // of lua functions, and it is extremely difficult to set up the + // pcall protection for every lua function that might allocate. + // If lua does this in an unprotected context, it will abort + // anyway, so the best we can do right now is guarantee an abort + // even in a protected context. + println!("Lua memory error, aborting!"); + process::abort() + } + ffi::LUA_ERRGCMM => { + // This should be impossible, or at least is indicative of an + // internal bug. Similarly to LUA_ERRMEM, this could indicate a + // longjmp out of rust code, so we just abort. + println!("Lua error during __gc, aborting!"); + process::abort() + } + _ => panic!("unrecognized lua error code"), + }) + } } } @@ -138,7 +182,7 @@ pub unsafe extern "C" fn destructor<T>(state: *mut ffi::lua_State) -> c_int { }) { Ok(r) => r, Err(p) => { - push_panic(state, p); + push_wrapped_panic(state, p); ffi::lua_error(state) } } @@ -155,68 +199,52 @@ where match catch_unwind(f) { Ok(Ok(r)) => r, Ok(Err(err)) => { - push_error(state, err); + push_wrapped_error(state, err); ffi::lua_error(state) } Err(p) => { - push_panic(state, p); + push_wrapped_panic(state, p); ffi::lua_error(state) } } } // Pushes a WrappedError::Error to the top of the stack -pub unsafe fn push_error(state: *mut ffi::lua_State, err: LuaError) { - push_wrapped_error(state, WrappedError::Error(err)); +pub unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: LuaError) { + do_push_wrapped_error(state, WrappedError::Error(err)); } // Pushes a WrappedError::Panic to the top of the stack -pub unsafe fn push_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) { - push_wrapped_error(state, WrappedError::Panic(Some(panic))); +pub unsafe fn push_wrapped_panic(state: *mut ffi::lua_State, panic: Box<Any + Send>) { + 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 // it is a WrappedError::Panic, clears the current stack and panics. -pub unsafe fn pop_error(state: *mut ffi::lua_State) -> LuaError { +pub unsafe fn pop_wrapped_error(state: *mut ffi::lua_State) -> LuaError { assert!( ffi::lua_gettop(state) > 0, - "pop_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" ); - // Pop an error off of the lua stack and interpret it as a wrapped error, or as a string. If - // the error cannot be interpreted as a string simply print that it was an unprintable error. - - if is_wrapped_error(state, -1) { - let userdata = ffi::lua_touserdata(state, -1); - match &mut *(userdata as *mut WrappedError) { - &mut WrappedError::Error(ref err) => { - let err = err.clone(); - ffi::lua_pop(state, 1); - err - } - &mut WrappedError::Panic(ref mut p) => { - let p = p.take().unwrap_or_else(|| { - Box::new("internal error: panic error used twice") - }); - ffi::lua_settop(state, 0); - resume_unwind(p) - } + let userdata = ffi::lua_touserdata(state, -1); + match &mut *(userdata as *mut WrappedError) { + &mut WrappedError::Error(ref err) => { + let err = err.clone(); + ffi::lua_pop(state, 1); + err } - - } else if let Some(s) = ffi::lua_tolstring(state, -1, ptr::null_mut()).as_ref() { - let error = CStr::from_ptr(s).to_str().unwrap().to_owned(); - ffi::lua_pop(state, 1); - // This seems terrible, but as far as I can tell, this is exactly what the stock lua - // repl does. - if error.ends_with("<eof>") { - LuaError::IncompleteStatement(error).into() - } else { - LuaError::ScriptError(error).into() + &mut WrappedError::Panic(ref mut p) => { + let p = p.take().unwrap_or_else(|| { + Box::new("internal error: panic error used twice") + }); + ffi::lua_settop(state, 0); + resume_unwind(p) } - - } else { - ffi::lua_pop(state, 1); - LuaError::ScriptError("<unprintable error>".to_owned()).into() } } @@ -231,13 +259,13 @@ pub unsafe fn pcall_with_traceback( unsafe extern "C" fn message_handler(state: *mut ffi::lua_State) -> c_int { if is_wrapped_error(state, 1) { if !is_panic_error(state, 1) { - let error = pop_error(state); + let error = pop_wrapped_error(state); ffi::luaL_traceback(state, state, ptr::null(), 0); let traceback = CStr::from_ptr(ffi::lua_tolstring(state, -1, ptr::null_mut())) .to_str() .unwrap() .to_owned(); - push_error(state, LuaError::CallbackError(traceback, Arc::new(error))); + push_wrapped_error(state, LuaError::CallbackError(traceback, Arc::new(error))); } } else { let s = ffi::lua_tolstring(state, 1, ptr::null_mut()); @@ -267,13 +295,13 @@ pub unsafe fn resume_with_traceback( if res != ffi::LUA_OK && res != ffi::LUA_YIELD { if is_wrapped_error(state, 1) { if !is_panic_error(state, 1) { - let error = pop_error(state); + let error = pop_wrapped_error(state); ffi::luaL_traceback(from, state, ptr::null(), 0); let traceback = CStr::from_ptr(ffi::lua_tolstring(from, -1, ptr::null_mut())) .to_str() .unwrap() .to_owned(); - push_error(from, LuaError::CallbackError(traceback, Arc::new(error))); + push_wrapped_error(from, LuaError::CallbackError(traceback, Arc::new(error))); } } else { let s = ffi::lua_tolstring(state, 1, ptr::null_mut()); @@ -340,16 +368,14 @@ enum WrappedError { } // Pushes the given error or panic as a wrapped error onto the stack -unsafe fn push_wrapped_error(state: *mut ffi::lua_State, err: WrappedError) { +unsafe fn do_push_wrapped_error(state: *mut ffi::lua_State, err: WrappedError) { // Wrapped errors have a __tostring metamethod and a 'backtrace' normal // method. unsafe extern "C" fn error_tostring(state: *mut ffi::lua_State) -> c_int { callback_error(state, || { if !is_wrapped_error(state, -1) { - return Err(LuaError::ConversionError( - "not WrappedError in error method".to_owned(), - )); + panic!("error metatable on wrong userdata, impossible") } let userdata = ffi::lua_touserdata(state, -1); |