Description
Background
I'm building a Lua binding that using drop to pop the values from Lua stack. I'll start by explaining how to use Lua API to embed it in the application so people who are not familiar with it can understand the problem I faced. You can proceed to next section if you already know about this.
Lua itself is written in C and it provides C API to embed it. The API is stack-based. You pass the value to Lua by pushing it to Lua stack (not stack on the OS thread) and receive the value from Lua by get it from Lua stack. The following is an example to call a Lua function that return a string with foo
as the first argument and 5 as the second argument:
lua_pushstring(L, "foo");
lua_pushinteger(L, 5);
lua_call(L, 2, 1); // 2 is the number of pushed arguments and 1 is the number of return values.
const char *ret = luaL_checkstring(L, -1); // -1 is top of the stack.
lua_pop(L, 1); // Remove the returned value, which render ret pointer on the above invalid after this.
If the function does not return a string luaL_checkstring
will push an error message then do a long jump to Lua error handler using C longjmp
or using throw
if it was compiled as C++, which Lua error handler expect this error message on the top of the stack.
How my binding works
My binding requires Lua to compile as C++ so Drop
implementation on Rust type can free any resources automatically when Lua trigger an error. I also represent each of the item in Lua stack as a Rust type that has Drop
implementation:
pub struct Str<'p, P: Frame>(&'p mut P);
impl<'p, P: Frame> Str<'p, P> {
/// # Safety
/// Top of the stack must be a string.
pub(crate) unsafe fn new(p: &'p mut P) -> Self {
Self(p)
}
}
impl<P: Frame> Drop for Str<'_, P> {
fn drop(&mut self) {
unsafe { lua_pop(self.0.get(), 1) };
}
}
P
is the parent item in the stack so when drop
is running it is guarantee that top of the stack always in the same shape as when the value is constructed. This mechanism works really well since it is effectively zero cost. The only problem is it does not works when Lua trigger any error since top of the stack is the error message instead of the last constructed value. I can use std::uncaught_exceptions()
to check inside Drop
implementation but it is not an optimal solution. Currently I patched Lua source to put the error message somewhere else before it raising a C++ exception so top of the stack has the same shape when drop is running. It would be better if I can exclude Drop
implementation on the above type from running during unwind.