summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorkyren <kerriganw@gmail.com>2017-06-24 20:57:04 -0400
committerkyren <kerriganw@gmail.com>2017-06-24 20:57:04 -0400
commit3deb6df525369f8b298b8ce0649f7b1dce6d13df (patch)
treedd6571235a505f9f73479548fb169612be0d3862 /src
parent47db72cac47bf6fc3d09fa0747997a102d41df9c (diff)
downloadmlua-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.rs65
-rw-r--r--src/error.rs166
-rw-r--r--src/lua.rs70
-rw-r--r--src/tests.rs16
-rw-r--r--src/util.rs146
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)
+ }
+}
diff --git a/src/lua.rs b/src/lua.rs
index f8d2837..aa28aa8 100644
--- a/src/lua.rs
+++ b/src/lua.rs
@@ -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);