Lua 和异常黑客笔记 |
|
截至版本 5.1,构建为 C++。
struct lua_longjmp { struct lua_longjmp *previous; int dummy; volatile int status; };它的作用是跟踪受保护的调用堆栈。我们从一个空的全局缓冲区开始。
// chain a new local buffer, status = 0 // global buffer now points to it try { // call } catch (...) { // force local status to non 0 (e.g. -1) } // restore global buffer // return local status
// if empty global buffer then panic // store status in global buffer // throw its address !
如果它们确实没有在 Lua 级别被捕获,那么稍后捕获它们将从 Lua 的调用堆栈中暴力地展开,留下一个不一致的 Lua 状态。这就是我在调整 Lua 5.0.2 时发生的事情。
为了在将异常传递到另一端的同时确保一致的 Lua 状态,我们需要将它的副本存储在一个安全的地方,正常地展开到顶层,最后抛出副本。但是,由于捕获匹配类型(Stroustrup 14.3),我们在使用 `catch (...)` 时会丢失类型信息,从而失去复制泛型异常的能力。
为了实现上面讨论的复制机制,我们可以将外来异常的类型范围缩小到标准 `std::exception`。如果程序员需要捕获其他异常,他可以将它们包装到 `std::exception` 子类中。其余异常仍然必须被静默忽略。
此解决方案的优点是简单,并且适用于分层异常类(每个根类一个包装器)。但是,它仅限于两端都可以在 `std::exception` 包装上达成一致的情况。
注意:这意味着包装器生成器需要适应此方案。
作为临时解决方案,也可以将异常空间缩小到一组自定义异常,即在 Lua 构建时手动进行。
待办事项:检查 `std::exception` 的副本
回顾过去,它似乎是我们这种情况下的一个合适的解决方案,对于非正式应用程序也应该足够了。但是,它涉及错误系统之间的转换,因此存在冗余。
如果 Lua 源代码变得(或分叉)异常安全,我们可以使它能够从 Lua 中捕获 C++ 异常,无论是显式地还是通过绑定到最终的 Lua 异常方案。下一节概述了一个解决方案。
catch (struct lua_longjump * p_lj) { // force local status to non 0 } catch (...) { // do soft unwinding throw }
注意:`p_lj` 应该指向当前的 `lj`。
待办事项:断言。
这应该保证我们不会吃掉外部异常,除非有人扭曲决定从她的库中到处抛出 `(struct lua_longjump)*`。
为了设计我们的算法,我们需要识别 `pcall` 获取和释放的重要资源。
pcall 堆栈概述
当 pcall 嵌套时,顶层和底层会包裹。
D_pcall 清理
if (status != 0) { /* an error occurred? */ StkId oldtop = restorestack(L, old_top); luaF_close(L, oldtop); /* close eventual pending closures */ luaD_seterrorobj(L, status, oldtop); L->nCcalls = oldnCcalls; L->ci = restoreci(L, old_ci); L->base = L->ci->base; L->savedpc = L->ci->savedpc; L->allowhook = old_allowhooks; restore_stack_limit(L); } L->errfunc = old_errfunc;
它恢复先前获取的状态
// ptrdiff_t old_top, ptrdiff_t ef unsigned short oldnCcalls = L->nCcalls; ptrdiff_t old_ci = saveci(L, L->ci); lu_byte old_allowhooks = L->allowhook; ptrdiff_t old_errfunc = L->errfunc; L->errfunc = ef;
现在,我们可以使用 *资源获取即初始化* 模式在对象销毁时自动执行清理代码。这是与异常集成的自然方式(Stroustrup 14.4.1)。
Stroustrup 14.9 是一个基础。