diff --git a/src/lua.rs b/src/lua.rs index f23005e..5e5c048 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -292,7 +292,9 @@ impl<'lua> Function<'lua> { lua.push_value(lua.state, arg); } - ffi::lua_pushcclosure(lua.state, bind_call_impl, nargs + 2); + protect_lua_call(lua.state, nargs + 2, 1, |state| { + ffi::lua_pushcclosure(state, bind_call_impl, nargs + 2); + })?; Ok(Function(lua.pop_ref(lua.state))) }) @@ -547,9 +549,9 @@ impl Lua { /// Pass a `&str` slice to Lua, creating and returning an interned Lua string. pub fn create_string(&self, s: &str) -> Result { unsafe { - stack_guard(self.state, 0, || { + stack_err_guard(self.state, 0, || { check_stack(self.state, 2); - ffi::lua_pushlstring(self.state, s.as_ptr() as *const c_char, s.len()); + push_string(self.state, s)?; Ok(String(self.pop_ref(self.state))) }) } @@ -558,7 +560,7 @@ impl Lua { /// Creates and returns a new table. pub fn create_table(&self) -> Result { unsafe { - stack_guard(self.state, 0, || { + stack_err_guard(self.state, 0, || { check_stack(self.state, 4); protect_lua_call(self.state, 0, 1, |state| { ffi::lua_newtable(state); @@ -585,7 +587,9 @@ impl Lua { for (k, v) in cont { self.push_value(self.state, k.to_lua(self)?); self.push_value(self.state, v.to_lua(self)?); - ffi::lua_rawset(self.state, -3); + protect_lua_call(self.state, 3, 1, |state| { + ffi::lua_rawset(state, -3); + })?; } Ok(Table(self.pop_ref(self.state))) }) @@ -660,10 +664,12 @@ impl Lua { /// Equivalent to `coroutine.create`. pub fn create_thread<'lua>(&'lua self, func: Function<'lua>) -> Result> { unsafe { - stack_guard(self.state, 0, move || { + stack_err_guard(self.state, 0, move || { check_stack(self.state, 2); - let thread_state = ffi::lua_newthread(self.state); + let thread_state = protect_lua_call(self.state, 0, 1, |state| { + ffi::lua_newthread(state) + })?; self.push_ref(thread_state, &func.0); Ok(Thread(self.pop_ref(self.state))) @@ -680,7 +686,7 @@ impl Lua { stack_err_guard(self.state, 0, move || { check_stack(self.state, 3); - push_userdata::>(self.state, RefCell::new(data)); + push_userdata::>(self.state, RefCell::new(data))?; ffi::lua_rawgeti( self.state, @@ -713,11 +719,14 @@ impl Lua { match v { Value::String(s) => Ok(s), v => unsafe { - stack_guard(self.state, 0, || { + stack_err_guard(self.state, 0, || { check_stack(self.state, 2); let ty = v.type_name(); self.push_value(self.state, v); - if ffi::lua_tostring(self.state, -1).is_null() { + let s = protect_lua_call(self.state, 1, 1, |state| { + ffi::lua_tostring(state, -1) + })?; + if s.is_null() { ffi::lua_pop(self.state, 1); Err(Error::FromLuaConversionError { from: ty, @@ -989,37 +998,50 @@ impl Lua { }; T::add_methods(&mut methods); - ffi::lua_newtable(self.state); + protect_lua_call(self.state, 0, 1, |state| { + ffi::lua_newtable(state); + })?; let has_methods = !methods.methods.is_empty(); if has_methods { - push_string(self.state, "__index"); - ffi::lua_newtable(self.state); + push_string(self.state, "__index")?; + protect_lua_call(self.state, 0, 1, |state| { + ffi::lua_newtable(state); + })?; for (k, m) in methods.methods { - push_string(self.state, &k); + push_string(self.state, &k)?; self.push_value( self.state, Value::Function(self.create_callback_function(m)?), ); - ffi::lua_rawset(self.state, -3); + protect_lua_call(self.state, 3, 1, |state| { + ffi::lua_rawset(state, -3); + })?; } - ffi::lua_rawset(self.state, -3); + protect_lua_call(self.state, 3, 1, |state| { + ffi::lua_rawset(state, -3); + })?; } for (k, m) in methods.meta_methods { if k == MetaMethod::Index && has_methods { - push_string(self.state, "__index"); + push_string(self.state, "__index")?; ffi::lua_pushvalue(self.state, -1); ffi::lua_gettable(self.state, -3); self.push_value( self.state, Value::Function(self.create_callback_function(m)?), ); - ffi::lua_pushcclosure(self.state, meta_index_impl, 2); - ffi::lua_rawset(self.state, -3); + protect_lua_call(self.state, 2, 1, |state| { + ffi::lua_pushcclosure(state, meta_index_impl, 2); + })?; + + protect_lua_call(self.state, 3, 1, |state| { + ffi::lua_rawset(state, -3); + })?; } else { let name = match k { MetaMethod::Add => "__add", @@ -1046,22 +1068,28 @@ impl Lua { MetaMethod::Call => "__call", MetaMethod::ToString => "__tostring", }; - push_string(self.state, name); + push_string(self.state, name)?; self.push_value( self.state, Value::Function(self.create_callback_function(m)?), ); - ffi::lua_rawset(self.state, -3); + protect_lua_call(self.state, 3, 1, |state| { + ffi::lua_rawset(state, -3); + })?; } } - push_string(self.state, "__gc"); + push_string(self.state, "__gc")?; ffi::lua_pushcfunction(self.state, userdata_destructor::>); - ffi::lua_rawset(self.state, -3); + protect_lua_call(self.state, 3, 1, |state| { + ffi::lua_rawset(state, -3); + })?; - push_string(self.state, "__metatable"); + push_string(self.state, "__metatable")?; ffi::lua_pushboolean(self.state, 0); - ffi::lua_rawset(self.state, -3); + protect_lua_call(self.state, 3, 1, |state| { + ffi::lua_rawset(state, -3); + })?; let id = ffi::luaL_ref(self.state, ffi::LUA_REGISTRYINDEX); (*registered_userdata).insert(TypeId::of::(), id); @@ -1094,6 +1122,8 @@ impl Lua { let state = ffi::lua_newstate(allocator, ptr::null_mut()); + // Ignores or `unwrap()`s 'm' errors, because this is assuming that nothing in the lua + // standard library will have a `__gc` metamethod error. stack_guard(state, 0, || { // Do not open the debug library, it can be used to cause unsafety. ffi::luaL_requiref(state, cstr!("_G"), ffi::luaopen_base, 1); @@ -1119,11 +1149,11 @@ impl Lua { &LUA_USERDATA_REGISTRY_KEY as *const u8 as *mut c_void, ); - push_userdata::>(state, HashMap::new()); + push_userdata::>(state, HashMap::new()).unwrap(); ffi::lua_newtable(state); - push_string(state, "__gc"); + push_string(state, "__gc").unwrap(); ffi::lua_pushcfunction(state, userdata_destructor::>); ffi::lua_rawset(state, -3); @@ -1140,11 +1170,11 @@ impl Lua { ffi::lua_newtable(state); - push_string(state, "__gc"); + push_string(state, "__gc").unwrap(); ffi::lua_pushcfunction(state, userdata_destructor::>); ffi::lua_rawset(state, -3); - push_string(state, "__metatable"); + push_string(state, "__metatable").unwrap(); ffi::lua_pushboolean(state, 0); ffi::lua_rawset(state, -3); @@ -1155,15 +1185,15 @@ impl Lua { ffi::lua_rawgeti(state, ffi::LUA_REGISTRYINDEX, ffi::LUA_RIDX_GLOBALS); - push_string(state, "pcall"); + push_string(state, "pcall").unwrap(); ffi::lua_pushcfunction(state, safe_pcall); ffi::lua_rawset(state, -3); - push_string(state, "xpcall"); + push_string(state, "xpcall").unwrap(); ffi::lua_pushcfunction(state, safe_xpcall); ffi::lua_rawset(state, -3); - push_string(state, "setmetatable"); + push_string(state, "setmetatable").unwrap(); ffi::lua_pushcfunction(state, safe_setmetatable); ffi::lua_rawset(state, -3); @@ -1217,10 +1247,10 @@ impl Lua { } unsafe { - stack_guard(self.state, 0, move || { + stack_err_guard(self.state, 0, move || { check_stack(self.state, 2); - push_userdata::>(self.state, RefCell::new(func)); + push_userdata::>(self.state, RefCell::new(func))?; ffi::lua_pushlightuserdata( self.state, @@ -1229,7 +1259,9 @@ impl Lua { ffi::lua_gettable(self.state, ffi::LUA_REGISTRYINDEX); ffi::lua_setmetatable(self.state, -2); - ffi::lua_pushcclosure(self.state, callback_call_impl, 1); + protect_lua_call(self.state, 1, 1, |state| { + ffi::lua_pushcclosure(state, callback_call_impl, 1); + })?; Ok(Function(self.pop_ref(self.state))) }) diff --git a/src/string.rs b/src/string.rs index 945a25a..bfc7231 100644 --- a/src/string.rs +++ b/src/string.rs @@ -77,6 +77,8 @@ impl<'lua> String<'lua> { assert_eq!(ffi::lua_type(lua.state, -1), ffi::LUA_TSTRING); let mut size = 0; + // This will not trigger a 'm' error, because the reference is guaranteed to be of + // string type let data = ffi::lua_tolstring(lua.state, -1, &mut size); ffi::lua_pop(lua.state, 1); diff --git a/src/table.rs b/src/table.rs index f4339b2..7347b7f 100644 --- a/src/table.rs +++ b/src/table.rs @@ -129,8 +129,9 @@ impl<'lua> Table<'lua> { lua.push_ref(lua.state, &self.0); lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(lua.state, value.to_lua(lua)?); - ffi::lua_rawset(lua.state, -3); - ffi::lua_pop(lua.state, 1); + protect_lua_call(lua.state, 3, 0, |state| { + ffi::lua_rawset(state, -3); + })?; Ok(()) }) } diff --git a/src/util.rs b/src/util.rs index 1f49378..2f4a363 100644 --- a/src/util.rs +++ b/src/util.rs @@ -151,21 +151,6 @@ where } } -// Runs the given function with the Lua garbage collector disabled. `rlua` assumes that all memory -// errors are aborts, so in this way, 'm' functions that may also cause a `__gc` metamethod error -// are guaranteed not to cause a Lua error (longjmp). The given function should never panic or -// longjmp, because this could inadverntently disable the gc. -pub unsafe fn gc_guard R>(state: *mut ffi::lua_State, f: F) -> R { - if ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0) != 0 { - ffi::lua_gc(state, ffi::LUA_GCSTOP, 0); - let r = f(); - ffi::lua_gc(state, ffi::LUA_GCRESTART, 0); - r - } else { - f() - } -} - // Pops an error off of the stack and returns it. If the error is actually a WrappedPanic, clears // the current lua stack and continues the panic. If the error on the top of the stack is actually // a WrappedError, just returns it. Otherwise, interprets the error as the appropriate lua error. @@ -231,13 +216,18 @@ pub unsafe fn pop_error(state: *mut ffi::lua_State, err_code: c_int) -> Error { } } -pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) { - ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); +pub unsafe fn push_string(state: *mut ffi::lua_State, s: &str) -> Result<()> { + protect_lua_call(state, 0, 1, |state| { + ffi::lua_pushlstring(state, s.as_ptr() as *const c_char, s.len()); + }) } -pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) { - let ud = ffi::lua_newuserdata(state, mem::size_of::>()) as *mut Option; - ptr::write(ud, Some(t)); +pub unsafe fn push_userdata(state: *mut ffi::lua_State, t: T) -> Result<()> { + let mut t = Some(t); + protect_lua_call(state, 0, 1, |state| { + let ud = ffi::lua_newuserdata(state, mem::size_of::>()) as *mut Option; + ptr::write(ud, t.take()); + }) } pub unsafe fn get_userdata(state: *mut ffi::lua_State, index: c_int) -> *mut T { @@ -549,17 +539,17 @@ unsafe fn get_error_metatable(state: *mut ffi::lua_State) -> c_int { ffi::lua_pushstring(state, cstr!("__gc")); ffi::lua_pushcfunction(state, userdata_destructor::); - ffi::lua_settable(state, -3); + ffi::lua_rawset(state, -3); ffi::lua_pushstring(state, cstr!("__tostring")); ffi::lua_pushcfunction(state, error_tostring); - ffi::lua_settable(state, -3); + ffi::lua_rawset(state, -3); ffi::lua_pushstring(state, cstr!("__metatable")); ffi::lua_pushboolean(state, 0); - ffi::lua_settable(state, -3); + ffi::lua_rawset(state, -3); - ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX); + ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); }) } @@ -590,15 +580,32 @@ unsafe fn get_panic_metatable(state: *mut ffi::lua_State) -> c_int { ffi::lua_pushstring(state, cstr!("__gc")); ffi::lua_pushcfunction(state, userdata_destructor::); - ffi::lua_settable(state, -3); + ffi::lua_rawset(state, -3); ffi::lua_pushstring(state, cstr!("__metatable")); ffi::lua_pushboolean(state, 0); - ffi::lua_settable(state, -3); + ffi::lua_rawset(state, -3); - ffi::lua_settable(state, ffi::LUA_REGISTRYINDEX); + ffi::lua_rawset(state, ffi::LUA_REGISTRYINDEX); }); } ffi::LUA_TTABLE } + +// Runs the given function with the Lua garbage collector disabled. `rlua` assumes that all memory +// errors are aborts, so in this way, 'm' functions that may also cause a `__gc` metamethod error +// are guaranteed not to cause a Lua error (longjmp). The given function should never panic or +// longjmp, because this could inadverntently disable the gc. This is useful when error handling +// must allocate, and `__gc` errors at that time would shadow more important errors, or be extremely +// difficult to handle safely. +unsafe fn gc_guard R>(state: *mut ffi::lua_State, f: F) -> R { + if ffi::lua_gc(state, ffi::LUA_GCISRUNNING, 0) != 0 { + ffi::lua_gc(state, ffi::LUA_GCSTOP, 0); + let r = f(); + ffi::lua_gc(state, ffi::LUA_GCRESTART, 0); + r + } else { + f() + } +}