Remove error_guard

Replace with custom protected versions of lua ffi functions.
This commit is contained in:
kyren 2017-07-23 13:41:46 -04:00
parent 91dbbfe759
commit 44c99ea1b9
2 changed files with 159 additions and 112 deletions

View File

@ -223,14 +223,11 @@ impl<'lua> LuaTable<'lua> {
let key = key.to_lua(lua)?; let key = key.to_lua(lua)?;
let value = value.to_lua(lua)?; let value = value.to_lua(lua)?;
unsafe { unsafe {
check_stack(lua.state, 5); check_stack(lua.state, 7);
lua.push_ref(lua.state, &self.0); lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key); lua.push_value(lua.state, key);
lua.push_value(lua.state, value); lua.push_value(lua.state, value);
error_guard(lua.state, 3, |state| { psettable(lua.state, -3)?;
ffi::lua_settable(state, -3);
Ok(())
})?;
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);
Ok(()) Ok(())
} }
@ -245,16 +242,15 @@ impl<'lua> LuaTable<'lua> {
let lua = self.0.lua; let lua = self.0.lua;
let key = key.to_lua(lua)?; let key = key.to_lua(lua)?;
unsafe { unsafe {
check_stack(lua.state, 4); stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.0); lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key.to_lua(lua)?); lua.push_value(lua.state, key.to_lua(lua)?);
error_guard(lua.state, 2, |state| { pgettable(lua.state, -2)?;
ffi::lua_gettable(state, -2);
Ok(())
})?;
let res = lua.pop_value(lua.state); let res = lua.pop_value(lua.state);
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);
V::from_lua(res, lua) V::from_lua(res, lua)
})
} }
} }
@ -263,16 +259,15 @@ impl<'lua> LuaTable<'lua> {
let lua = self.0.lua; let lua = self.0.lua;
let key = key.to_lua(lua)?; let key = key.to_lua(lua)?;
unsafe { unsafe {
check_stack(lua.state, 4); stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 5);
lua.push_ref(lua.state, &self.0); lua.push_ref(lua.state, &self.0);
lua.push_value(lua.state, key); lua.push_value(lua.state, key);
error_guard(lua.state, 2, |state| { pgettable(lua.state, -2)?;
ffi::lua_gettable(state, -2);
Ok(())
})?;
let has = ffi::lua_isnil(lua.state, -1) == 0; let has = ffi::lua_isnil(lua.state, -1) == 0;
ffi::lua_pop(lua.state, 2); ffi::lua_pop(lua.state, 2);
Ok(has) Ok(has)
})
} }
} }
@ -314,13 +309,13 @@ impl<'lua> LuaTable<'lua> {
pub fn len(&self) -> LuaResult<LuaInteger> { pub fn len(&self) -> LuaResult<LuaInteger> {
let lua = self.0.lua; let lua = self.0.lua;
unsafe { unsafe {
stack_err_guard(lua.state, 0, || {
check_stack(lua.state, 3); check_stack(lua.state, 3);
lua.push_ref(lua.state, &self.0); lua.push_ref(lua.state, &self.0);
let len = error_guard(lua.state, 1, |state| { let len = plen(lua.state, -1)?;
Ok(ffi::luaL_len(state, -1))
})?;
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);
Ok(len) Ok(len)
})
} }
} }
@ -386,13 +381,18 @@ where
let lua = self.table.lua; let lua = self.table.lua;
unsafe { unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 6); check_stack(lua.state, 6);
lua.push_ref(lua.state, &self.table); lua.push_ref(lua.state, &self.table);
lua.push_ref(lua.state, &next_key); lua.push_ref(lua.state, &next_key);
match error_guard(lua.state, 2, |state| Ok(ffi::lua_next(state, -2) != 0)) { match pnext(lua.state, -2) {
Ok(true) => { Ok(0) => {
ffi::lua_pop(lua.state, 1);
None
}
Ok(_) => {
ffi::lua_pushvalue(lua.state, -2); ffi::lua_pushvalue(lua.state, -2);
let key = lua.pop_value(lua.state); let key = lua.pop_value(lua.state);
let value = lua.pop_value(lua.state); let value = lua.pop_value(lua.state);
@ -405,12 +405,9 @@ where
Ok((key, value)) Ok((key, value))
})()) })())
} }
Ok(false) => {
ffi::lua_pop(lua.state, 1);
None
}
Err(e) => Some(Err(e)), Err(e) => Some(Err(e)),
} }
})
} }
} else { } else {
None None
@ -439,22 +436,24 @@ where
let lua = self.table.lua; let lua = self.table.lua;
unsafe { unsafe {
stack_guard(lua.state, 0, || {
check_stack(lua.state, 4); check_stack(lua.state, 4);
lua.push_ref(lua.state, &self.table); lua.push_ref(lua.state, &self.table);
match error_guard(lua.state, 1, |state| Ok(ffi::lua_geti(state, -1, index) != ffi::LUA_TNIL)) { match pgeti(lua.state, -1, index) {
Ok(true) => { Ok(ffi::LUA_TNIL) => {
ffi::lua_pop(lua.state, 2);
None
}
Ok(_) => {
let value = lua.pop_value(lua.state); let value = lua.pop_value(lua.state);
ffi::lua_pop(lua.state, 1); ffi::lua_pop(lua.state, 1);
self.index = Some(index + 1); self.index = Some(index + 1);
Some(V::from_lua(value, lua)) Some(V::from_lua(value, lua))
} }
Ok(false) => { Err(err) => Some(Err(err)),
ffi::lua_pop(lua.state, 2);
None
},
Err(e) => Some(Err(e)),
} }
})
} }
} else { } else {
None None

View File

@ -151,56 +151,104 @@ where
res res
} }
// Call the given rust function in a protected lua context, similar to pcall. The stack given to // Protected version of lua_gettable, uses 3 stack spaces, does not call checkstack.
// the protected function is a separate protected stack. This catches all calls to lua_error, but pub unsafe fn pgettable(state: *mut ffi::lua_State, index: c_int) -> LuaResult<c_int> {
// ffi functions that can call lua_error are still longjmps, and have all the same dangers as unsafe extern "C" fn gettable(state: *mut ffi::lua_State) -> c_int {
// longjmps, so extreme care must still be taken in code that uses this function. Does not call ffi::lua_gettable(state, -2);
// lua_checkstack, and uses 2 extra stack spaces. On error, the stack position is set to just below 1
// the given arguments. }
pub unsafe fn error_guard<F, R>(
state: *mut ffi::lua_State, let table_index = ffi::lua_absindex(state, index);
nargs: c_int,
func: F, ffi::lua_pushcfunction(state, gettable);
) -> LuaResult<R> ffi::lua_pushvalue(state, table_index);
where ffi::lua_pushvalue(state, -3);
F: FnOnce(*mut ffi::lua_State) -> LuaResult<R> + UnwindSafe, ffi::lua_remove(state, -4);
{
unsafe extern "C" fn call_impl<F>(state: *mut ffi::lua_State) -> c_int handle_error(state, pcall_with_traceback(state, 2, 1))?;
where Ok(ffi::lua_type(state, -1))
F: FnOnce(*mut ffi::lua_State) -> c_int, }
{
let func = ffi::lua_touserdata(state, -1) as *mut F; // Protected version of lua_settable, uses 4 stack spaces, does not call checkstack.
let func = mem::replace(&mut *func, mem::uninitialized()); 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 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<ffi::lua_Integer> {
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); ffi::lua_pop(state, 1);
func(state) Ok(len)
} }
unsafe fn cpcall<F>( // Protected version of lua_geti, uses 3 stack spaces, does not call checkstack.
state: *mut ffi::lua_State, pub unsafe fn pgeti(state: *mut ffi::lua_State, index: c_int, i: ffi::lua_Integer) -> LuaResult<c_int> {
nargs: c_int, unsafe extern "C" fn geti(state: *mut ffi::lua_State) -> c_int {
mut func: F, let i = ffi::lua_tointeger(state, -1);
) -> LuaResult<()> ffi::lua_geti(state, -2, i);
where 1
F: FnOnce(*mut ffi::lua_State) -> c_int,
{
ffi::lua_pushcfunction(state, call_impl::<F>);
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 mut res = None; let table_index = ffi::lua_absindex(state, index);
let top = ffi::lua_gettop(state);
if let Err(err) = cpcall(state, nargs, |state| { ffi::lua_pushcfunction(state, geti);
res = Some(callback_error(state, || func(state))); ffi::lua_pushvalue(state, table_index);
let c = ffi::lua_gettop(state); ffi::lua_pushinteger(state, i);
c
}) { handle_error(state, pcall_with_traceback(state, 2, 1))?;
ffi::lua_settop(state, top - nargs); Ok(ffi::lua_type(state, -1))
Err(err) }
// 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<c_int> {
unsafe extern "C" fn next(state: *mut ffi::lua_State) -> c_int {
if ffi::lua_next(state, -2) == 0 {
0
} else { } else {
Ok(res.unwrap()) 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(1)
} }
} }