From 44c99ea1b9d73c52b83ad9540b1599597cd6b764 Mon Sep 17 00:00:00 2001 From: kyren Date: Sun, 23 Jul 2017 13:41:46 -0400 Subject: [PATCH] Remove error_guard Replace with custom protected versions of lua ffi functions. --- src/lua.rs | 133 +++++++++++++++++++++++++------------------------- src/util.rs | 138 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 159 insertions(+), 112 deletions(-) diff --git a/src/lua.rs b/src/lua.rs index 4ed9bd2..a881cec 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -223,14 +223,11 @@ impl<'lua> LuaTable<'lua> { let key = key.to_lua(lua)?; let value = value.to_lua(lua)?; unsafe { - check_stack(lua.state, 5); + check_stack(lua.state, 7); lua.push_ref(lua.state, &self.0); lua.push_value(lua.state, key); lua.push_value(lua.state, value); - error_guard(lua.state, 3, |state| { - ffi::lua_settable(state, -3); - Ok(()) - })?; + psettable(lua.state, -3)?; ffi::lua_pop(lua.state, 1); Ok(()) } @@ -245,16 +242,15 @@ impl<'lua> LuaTable<'lua> { let lua = self.0.lua; let key = key.to_lua(lua)?; unsafe { - check_stack(lua.state, 4); - lua.push_ref(lua.state, &self.0); - lua.push_value(lua.state, key.to_lua(lua)?); - error_guard(lua.state, 2, |state| { - ffi::lua_gettable(state, -2); - Ok(()) - })?; - let res = lua.pop_value(lua.state); - ffi::lua_pop(lua.state, 1); - V::from_lua(res, lua) + stack_err_guard(lua.state, 0, || { + check_stack(lua.state, 5); + lua.push_ref(lua.state, &self.0); + lua.push_value(lua.state, key.to_lua(lua)?); + pgettable(lua.state, -2)?; + let res = lua.pop_value(lua.state); + ffi::lua_pop(lua.state, 1); + V::from_lua(res, lua) + }) } } @@ -263,16 +259,15 @@ impl<'lua> LuaTable<'lua> { let lua = self.0.lua; let key = key.to_lua(lua)?; unsafe { - check_stack(lua.state, 4); - lua.push_ref(lua.state, &self.0); - lua.push_value(lua.state, key); - error_guard(lua.state, 2, |state| { - ffi::lua_gettable(state, -2); - Ok(()) - })?; - let has = ffi::lua_isnil(lua.state, -1) == 0; - ffi::lua_pop(lua.state, 2); - Ok(has) + stack_err_guard(lua.state, 0, || { + check_stack(lua.state, 5); + lua.push_ref(lua.state, &self.0); + lua.push_value(lua.state, key); + pgettable(lua.state, -2)?; + let has = ffi::lua_isnil(lua.state, -1) == 0; + ffi::lua_pop(lua.state, 2); + Ok(has) + }) } } @@ -314,13 +309,13 @@ impl<'lua> LuaTable<'lua> { pub fn len(&self) -> LuaResult { let lua = self.0.lua; unsafe { - check_stack(lua.state, 3); - lua.push_ref(lua.state, &self.0); - let len = error_guard(lua.state, 1, |state| { - Ok(ffi::luaL_len(state, -1)) - })?; - ffi::lua_pop(lua.state, 1); - Ok(len) + stack_err_guard(lua.state, 0, || { + check_stack(lua.state, 3); + lua.push_ref(lua.state, &self.0); + let len = plen(lua.state, -1)?; + ffi::lua_pop(lua.state, 1); + Ok(len) + }) } } @@ -386,31 +381,33 @@ where let lua = self.table.lua; unsafe { - check_stack(lua.state, 6); + stack_guard(lua.state, 0, || { + check_stack(lua.state, 6); - lua.push_ref(lua.state, &self.table); - lua.push_ref(lua.state, &next_key); + lua.push_ref(lua.state, &self.table); + lua.push_ref(lua.state, &next_key); - match error_guard(lua.state, 2, |state| Ok(ffi::lua_next(state, -2) != 0)) { - Ok(true) => { - ffi::lua_pushvalue(lua.state, -2); - let key = lua.pop_value(lua.state); - let value = lua.pop_value(lua.state); - self.next_key = Some(lua.pop_ref(lua.state)); - ffi::lua_pop(lua.state, 1); + match pnext(lua.state, -2) { + Ok(0) => { + ffi::lua_pop(lua.state, 1); + None + } + Ok(_) => { + ffi::lua_pushvalue(lua.state, -2); + let key = lua.pop_value(lua.state); + let value = lua.pop_value(lua.state); + self.next_key = Some(lua.pop_ref(lua.state)); + ffi::lua_pop(lua.state, 1); - Some((|| { - let key = K::from_lua(key, lua)?; - let value = V::from_lua(value, lua)?; - Ok((key, value)) - })()) + Some((|| { + let key = K::from_lua(key, lua)?; + let value = V::from_lua(value, lua)?; + Ok((key, value)) + })()) + } + Err(e) => Some(Err(e)), } - Ok(false) => { - ffi::lua_pop(lua.state, 1); - None - } - Err(e) => Some(Err(e)), - } + }) } } else { None @@ -439,22 +436,24 @@ where let lua = self.table.lua; unsafe { - check_stack(lua.state, 4); + stack_guard(lua.state, 0, || { + check_stack(lua.state, 4); - lua.push_ref(lua.state, &self.table); - match error_guard(lua.state, 1, |state| Ok(ffi::lua_geti(state, -1, index) != ffi::LUA_TNIL)) { - Ok(true) => { - let value = lua.pop_value(lua.state); - ffi::lua_pop(lua.state, 1); - self.index = Some(index + 1); - Some(V::from_lua(value, lua)) + lua.push_ref(lua.state, &self.table); + match pgeti(lua.state, -1, index) { + Ok(ffi::LUA_TNIL) => { + ffi::lua_pop(lua.state, 2); + None + } + Ok(_) => { + let value = lua.pop_value(lua.state); + ffi::lua_pop(lua.state, 1); + self.index = Some(index + 1); + Some(V::from_lua(value, lua)) + } + Err(err) => Some(Err(err)), } - Ok(false) => { - ffi::lua_pop(lua.state, 2); - None - }, - Err(e) => Some(Err(e)), - } + }) } } else { None diff --git a/src/util.rs b/src/util.rs index 8fdba9a..3777f49 100644 --- a/src/util.rs +++ b/src/util.rs @@ -151,56 +151,104 @@ where res } -// Call the given rust function in a protected lua context, similar to pcall. The stack given to -// the protected function is a separate protected stack. This catches all calls to lua_error, but -// ffi functions that can call lua_error are still longjmps, and have all the same dangers as -// longjmps, so extreme care must still be taken in code that uses this function. Does not call -// lua_checkstack, and uses 2 extra stack spaces. On error, the stack position is set to just below -// the given arguments. -pub unsafe fn error_guard( - state: *mut ffi::lua_State, - nargs: c_int, - func: F, -) -> LuaResult -where - F: FnOnce(*mut ffi::lua_State) -> LuaResult + UnwindSafe, -{ - unsafe extern "C" fn call_impl(state: *mut ffi::lua_State) -> c_int - where - F: FnOnce(*mut ffi::lua_State) -> c_int, - { - let func = ffi::lua_touserdata(state, -1) as *mut F; - let func = mem::replace(&mut *func, mem::uninitialized()); - ffi::lua_pop(state, 1); - func(state) +// Protected version of lua_gettable, uses 3 stack spaces, does not call checkstack. +pub unsafe fn pgettable(state: *mut ffi::lua_State, index: c_int) -> LuaResult { + unsafe extern "C" fn gettable(state: *mut ffi::lua_State) -> c_int { + ffi::lua_gettable(state, -2); + 1 } - unsafe fn cpcall( - state: *mut ffi::lua_State, - nargs: c_int, - mut func: F, - ) -> LuaResult<()> - where - F: FnOnce(*mut ffi::lua_State) -> c_int, - { - ffi::lua_pushcfunction(state, call_impl::); - ffi::lua_insert(state, -(nargs + 1)); - ffi::lua_pushlightuserdata(state, &mut func as *mut F as *mut c_void); - mem::forget(func); - handle_error(state, pcall_with_traceback(state, nargs + 1, ffi::LUA_MULTRET)) + let table_index = ffi::lua_absindex(state, index); + + ffi::lua_pushcfunction(state, gettable); + ffi::lua_pushvalue(state, table_index); + ffi::lua_pushvalue(state, -3); + ffi::lua_remove(state, -4); + + handle_error(state, pcall_with_traceback(state, 2, 1))?; + Ok(ffi::lua_type(state, -1)) +} + +// Protected version of lua_settable, uses 4 stack spaces, does not call checkstack. +pub unsafe fn psettable(state: *mut ffi::lua_State, index: c_int) -> LuaResult<()> { + unsafe extern "C" fn settable(state: *mut ffi::lua_State) -> c_int { + ffi::lua_settable(state, -3); + 0 } - let mut res = None; - let top = ffi::lua_gettop(state); - if let Err(err) = cpcall(state, nargs, |state| { - res = Some(callback_error(state, || func(state))); - let c = ffi::lua_gettop(state); - c - }) { - ffi::lua_settop(state, top - nargs); - Err(err) + let table_index = ffi::lua_absindex(state, index); + + ffi::lua_pushcfunction(state, settable); + ffi::lua_pushvalue(state, table_index); + ffi::lua_pushvalue(state, -4); + ffi::lua_pushvalue(state, -4); + ffi::lua_remove(state, -5); + ffi::lua_remove(state, -5); + + handle_error(state, pcall_with_traceback(state, 3, 0))?; + Ok(()) +} + +// Protected version of luaL_len, uses 2 stack spaces, does not call checkstack. +pub unsafe fn plen(state: *mut ffi::lua_State, index: c_int) -> LuaResult { + unsafe extern "C" fn len(state: *mut ffi::lua_State) -> c_int { + ffi::lua_pushinteger(state, ffi::luaL_len(state, -1)); + 1 + } + + let table_index = ffi::lua_absindex(state, index); + + ffi::lua_pushcfunction(state, len); + ffi::lua_pushvalue(state, table_index); + + handle_error(state, pcall_with_traceback(state, 1, 1))?; + let len = ffi::lua_tointeger(state, -1); + ffi::lua_pop(state, 1); + Ok(len) +} + +// Protected version of lua_geti, uses 3 stack spaces, does not call checkstack. +pub unsafe fn pgeti(state: *mut ffi::lua_State, index: c_int, i: ffi::lua_Integer) -> LuaResult { + unsafe extern "C" fn geti(state: *mut ffi::lua_State) -> c_int { + let i = ffi::lua_tointeger(state, -1); + ffi::lua_geti(state, -2, i); + 1 + } + + let table_index = ffi::lua_absindex(state, index); + + ffi::lua_pushcfunction(state, geti); + ffi::lua_pushvalue(state, table_index); + ffi::lua_pushinteger(state, i); + + handle_error(state, pcall_with_traceback(state, 2, 1))?; + Ok(ffi::lua_type(state, -1)) +} + +// Protected version of lua_next, uses 3 stack spaces, does not call checkstack. +pub unsafe fn pnext(state: *mut ffi::lua_State, index: c_int) -> LuaResult { + unsafe extern "C" fn next(state: *mut ffi::lua_State) -> c_int { + if ffi::lua_next(state, -2) == 0 { + 0 + } else { + 2 + } + } + + let table_index = ffi::lua_absindex(state, index); + + ffi::lua_pushcfunction(state, next); + ffi::lua_pushvalue(state, table_index); + ffi::lua_pushvalue(state, -3); + ffi::lua_remove(state, -4); + + let stack_start = ffi::lua_gettop(state) - 3; + handle_error(state, pcall_with_traceback(state, 2, ffi::LUA_MULTRET))?; + let nresults = ffi::lua_gettop(state) - stack_start; + if nresults == 0 { + Ok(0) } else { - Ok(res.unwrap()) + Ok(1) } }