[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: 3rd UPDATE: try-catch-finally for Lua, 02/1/2008
- From: "Hu Qiwei" <huqiwei@...>
- Date: Fri, 1 Feb 2008 11:43:18 +0800
The attachment is the ver.3 of try-catch-finally patch for lua 5.1.3.
I will not release it to "Power Patches" section because it still
needs to be improved.
Syntax of try-catch-finally please refer to my previous mail.
Changes:
1) "return" and "break" in finally-block NEVER allowed. consider the
following case:
try
return x, y
finally
return z
end
That's definitely ambiguous and practically unnecessary. But you can
"break" or "return" in try-block and catch-block anywhere you like.
2) bug fix: finally-block may destroy the return values.
3) Scoping change: local variables defined in try-block will live till
the end of finally- or catch- block. by popular demand.
There might be still some memory/stack/gc issues. Any comments or
bug-reporting will be appreciated. You can also modify the code
yourself, just post your ideas/changes/code slices to the mailing list
then.
I'll have a trip to Egypt for my vacation tomorrow, and may not be
able to reply the mail in the following 2 or more weeks. So everyone,
good luck and enjoy.
Hu
diff -urN src2/ldo.c src/ldo.c
--- src2/ldo.c 2008-01-19 06:31:22.000000000 +0800
+++ src/ldo.c 2008-02-01 08:51:05.727304000 +0800
@@ -40,14 +40,6 @@
*/
-/* chain list of long jump buffers */
-struct lua_longjmp {
- struct lua_longjmp *previous;
- luai_jmpbuf b;
- volatile int status; /* error code */
-};
-
-
void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
switch (errcode) {
case LUA_ERRMEM: {
@@ -90,8 +82,20 @@
L->errorJmp = NULL;
}
+void luaD_freefstack (lua_State *L) {
+ struct lua_longjmp *pj, *pprev;
+ /* free fstack */
+ pj = L->fstack;
+ while(pj) {
+ pprev = pj->previous;
+ luaM_free(L, pj);
+ pj = pprev;
+ }
+ L->fstack = NULL;
+}
-void luaD_throw (lua_State *L, int errcode) {
+void luaD_throw (lua_State *L, int errcode) {
+ luaD_freefstack(L); /* error on error, free all _finally_ stack */
if (L->errorJmp) {
L->errorJmp->status = errcode;
LUAI_THROW(L, L->errorJmp);
@@ -109,7 +113,8 @@
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) {
- struct lua_longjmp lj;
+ struct lua_longjmp lj;
+ lj.type = JMPTYPE_LONGJMP;
lj.status = 0;
lj.previous = L->errorJmp; /* chain new error handler */
L->errorJmp = &lj;
diff -urN src2/ldo.h src/ldo.h
--- src2/ldo.h 2007-12-27 21:02:26.000000000 +0800
+++ src/ldo.h 2008-02-01 01:27:24.457798400 +0800
@@ -11,6 +11,29 @@
#include "lobject.h"
#include "lstate.h"
#include "lzio.h"
+
+#include <setjmp.h>
+
+#define JMPTYPE_LONGJMP 0
+#define JMPTYPE_TRY 1
+
+/* chain list of long jump buffers */
+struct lua_longjmp {
+ struct lua_longjmp *previous;
+ luai_jmpbuf b;
+ volatile int status; /* error code */
+
+ int type; /* JMPTYPE_* */
+ const Instruction *dest;
+ ptrdiff_t errobj; /* holds error object */
+ int opcode;
+ ptrdiff_t old_ci;
+ ptrdiff_t old_errfunc;
+ int old_top;
+ int old_nexeccalls;
+ unsigned short oldnCcalls;
+ lu_byte old_allowhooks;
+};
#define luaD_checkstack(L,n) \
@@ -47,7 +70,8 @@
LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize);
LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize);
LUAI_FUNC void luaD_growstack (lua_State *L, int n);
-
+
+LUAI_FUNC void luaD_freefstack (lua_State *L);
LUAI_FUNC void luaD_throw (lua_State *L, int errcode);
LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud);
diff -urN src2/llex.c src/llex.c
--- src2/llex.c 2007-12-27 21:02:26.000000000 +0800
+++ src/llex.c 2008-01-30 09:37:51.007926400 +0800
@@ -35,10 +35,10 @@
/* ORDER RESERVED */
const char *const luaX_tokens [] = {
- "and", "break", "do", "else", "elseif",
- "end", "false", "for", "function", "if",
+ "and", "break", "catch", "do", "else", "elseif",
+ "end", "false", "finally", "for", "function", "if",
"in", "local", "nil", "not", "or", "repeat",
- "return", "then", "true", "until", "while",
+ "return", "then", "true", "try", "until", "while",
"..", "...", "==", ">=", "<=", "~=",
"<number>", "<name>", "<string>", "<eof>",
NULL
diff -urN src2/llex.h src/llex.h
--- src2/llex.h 2007-12-27 21:02:26.000000000 +0800
+++ src/llex.h 2008-01-30 09:37:37.628688000 +0800
@@ -23,10 +23,10 @@
*/
enum RESERVED {
/* terminal symbols denoted by reserved words */
- TK_AND = FIRST_RESERVED, TK_BREAK,
- TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION,
+ TK_AND = FIRST_RESERVED, TK_BREAK, TK_CATCH,
+ TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FINALLY, TK_FOR, TK_FUNCTION,
TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT,
- TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE,
+ TK_RETURN, TK_THEN, TK_TRUE, TK_TRY, TK_UNTIL, TK_WHILE,
/* other terminal symbols */
TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER,
TK_NAME, TK_STRING, TK_EOS
diff -urN src2/lopcodes.c src/lopcodes.c
--- src2/lopcodes.c 2007-12-27 21:02:26.000000000 +0800
+++ src/lopcodes.c 2008-01-31 21:10:55.378467200 +0800
@@ -36,7 +36,12 @@
"NOT",
"LEN",
"CONCAT",
- "JMP",
+ "JMP",
+ "TRY",
+ "TRYCATCH",
+ "TRYFINALLY",
+ "EXITTRY",
+ "RETFIN",
"EQ",
"LT",
"LE",
@@ -83,6 +88,11 @@
,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */
,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */
,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */
+ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_TRY */
+ ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_TRYCATCH */
+ ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_TRYFIN */
+ ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_EXITTRY */
+ ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_RETFIN */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */
,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */
diff -urN src2/lopcodes.h src/lopcodes.h
--- src2/lopcodes.h 2007-12-27 21:02:26.000000000 +0800
+++ src/lopcodes.h 2008-01-31 21:10:33.667248000 +0800
@@ -181,7 +181,12 @@
OP_CONCAT,/* A B C R(A) := R(B).. ... ..R(C) */
OP_JMP,/* sBx pc+=sBx */
-
+OP_TRY,/* sBx pc+=sBx */
+OP_TRYCATCH, /* A sBx R(A) := errorobj */
+OP_TRYFIN, /* A sBx R(A) := errorobj */
+OP_EXITTRY,
+OP_RETFIN, /* A B C R(A) := errorobj */
+
OP_EQ,/* A B C if ((RK(B) == RK(C)) ~= A) then pc++ */
OP_LT,/* A B C if ((RK(B) < RK(C)) ~= A) then pc++ */
OP_LE,/* A B C if ((RK(B) <= RK(C)) ~= A) then pc++ */
diff -urN src2/lparser.c src/lparser.c
--- src2/lparser.c 2007-12-28 23:32:24.000000000 +0800
+++ src/lparser.c 2008-02-01 10:36:25.584518400 +0800
@@ -42,7 +42,7 @@
int breaklist; /* list of jumps out of this loop */
lu_byte nactvar; /* # active locals outside the breakable structure */
lu_byte upval; /* true if some variable in the block is an upvalue */
- lu_byte isbreakable; /* true if `block' is a loop */
+ lu_byte isbreakable; /* 0: normal block, 1: loop, 2: try, 3: finally */
} BlockCnt;
@@ -300,7 +300,7 @@
if (bl->upval)
luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0);
/* a block either controls scope or breaks (never both) */
- lua_assert(!bl->isbreakable || !bl->upval);
+ lua_assert(bl->isbreakable != 1|| !bl->upval);
lua_assert(bl->nactvar == fs->nactvar);
fs->freereg = fs->nactvar; /* free registers */
luaK_patchtohere(fs, bl->breaklist);
@@ -871,7 +871,8 @@
static int block_follow (int token) {
switch (token) {
case TK_ELSE: case TK_ELSEIF: case TK_END:
- case TK_UNTIL: case TK_EOS:
+ case TK_UNTIL: case TK_EOS:
+ case TK_CATCH: case TK_FINALLY:
return 1;
default: return 0;
}
@@ -976,7 +977,11 @@
FuncState *fs = ls->fs;
BlockCnt *bl = fs->bl;
int upval = 0;
- while (bl && !bl->isbreakable) {
+ while (bl && bl->isbreakable != 1) {
+ if (bl->isbreakable == 2)
+ luaK_codeABC(fs, OP_EXITTRY, 0, 0, 0);
+ else if (bl->isbreakable == 3)
+ luaX_syntaxerror(ls, "can't break in _finally_ clause");
upval |= bl->upval;
bl = bl->previous;
}
@@ -1160,7 +1165,72 @@
luaK_patchtohere(fs, escapelist);
check_match(ls, TK_END, TK_IF, line);
}
-
+
+static void trystat (LexState *ls, int line) {
+ /* trystat -> TRY block CATCH err DO block END */
+ FuncState *fs = ls->fs;
+ BlockCnt bl;
+ int base, pc, escapelist = NO_JUMP;
+
+ luaX_next(ls);
+
+ enterblock(fs, &bl, 2); /* try block */
+ base = fs->freereg;
+ new_localvarliteral(ls, "(error obj)", 0);
+ adjustlocalvars(ls, 1); /* error object */
+ luaK_reserveregs(fs, 1);
+
+ pc = luaK_codeAsBx(fs, OP_TRY, base, NO_JUMP);
+ chunk(ls);
+
+ if (ls->t.token == TK_CATCH) {
+ TString *varname;
+ int errobj;
+
+ luaK_codeABC(fs, OP_EXITTRY, 0, 0, 0);
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ SET_OPCODE(fs->f->code[pc], OP_TRYCATCH); /* change it to TRYCATCH */
+ luaK_patchtohere(fs, pc);
+ bl.isbreakable = 0;
+
+ // local err
+ luaX_next(ls); /* skip `catch' */
+ varname = str_checkname(ls); /* first variable name */
+
+ // do
+ checknext(ls, TK_DO);
+ errobj = fs->freereg;
+ new_localvar(ls, varname, 0);
+ adjustlocalvars(ls, 1);
+ luaK_reserveregs(fs, 1);
+ luaK_codeABC(fs, OP_MOVE, errobj, base, 0);
+
+ block(ls);
+
+ } else if (ls->t.token == TK_FINALLY) {
+ luaK_codeABC(fs, OP_EXITTRY, 0, 0, 0);
+ luaK_concat(fs, &escapelist, luaK_jump(fs));
+ SET_OPCODE(fs->f->code[pc], OP_TRYFIN); /* change it to TRYFIN */
+ luaK_patchtohere(fs, pc);
+ bl.isbreakable = 3;
+
+ luaX_next(ls); /* skip 'finally' */
+
+ block(ls);
+
+ luaK_codeABC(fs, OP_RETFIN, base, 0, 0); /* OP_ENDFIN jump to the return point */
+
+ } else {
+ luaK_codeABC(fs, OP_EXITTRY, 0, 0, 0);
+ luaK_concat(fs, &escapelist, pc);
+ }
+
+ leaveblock(fs);
+
+ luaK_patchtohere(fs, escapelist);
+ check_match(ls, TK_END, TK_TRY, line);
+}
+
static void localfunc (LexState *ls) {
expdesc v, b;
@@ -1238,8 +1308,11 @@
static void retstat (LexState *ls) {
/* stat -> RETURN explist */
FuncState *fs = ls->fs;
+ BlockCnt *bl = fs->bl;
expdesc e;
- int first, nret; /* registers with returned values */
+ int first, nret; /* registers with returned values */
+ int ret_in_try = 0;
+
luaX_next(ls); /* skip RETURN */
if (block_follow(ls->t.token) || ls->t.token == ';')
first = nret = 0; /* return no values */
@@ -1263,8 +1336,23 @@
lua_assert(nret == fs->freereg - first);
}
}
- }
- luaK_ret(fs, first, nret);
+ }
+
+ /* before return, we should exit all try-catch blocks */
+ while (bl) {
+ if (bl->isbreakable == 2) {
+ if (ret_in_try)
+ luaK_codeABC(fs, OP_EXITTRY, 0, 0, 0);
+ else {
+ ret_in_try = 1;
+ luaK_codeABC(fs, OP_EXITTRY, first, nret+1, 1); /* here we will save all return values */
+ }
+ } else if (bl->isbreakable == 3)
+ luaX_syntaxerror(ls, "can't return in _finally_ clause");
+ bl = bl->previous;
+ }
+
+ luaK_codeABC(fs, OP_RETURN, first, nret+1, ret_in_try);
}
@@ -1296,6 +1384,10 @@
case TK_FUNCTION: {
funcstat(ls, line); /* stat -> funcstat */
return 0;
+ }
+ case TK_TRY: {
+ trystat(ls, line);
+ return 0;
}
case TK_LOCAL: { /* stat -> localstat */
luaX_next(ls); /* skip LOCAL */
diff -urN src2/lstate.c src/lstate.c
--- src2/lstate.c 2008-01-03 23:20:40.000000000 +0800
+++ src/lstate.c 2008-02-01 01:29:13.634787200 +0800
@@ -54,13 +54,34 @@
L1->ci->func = L1->top;
setnilvalue(L1->top++); /* `function' entry for this `ci' */
L1->base = L1->ci->base = L1->top;
- L1->ci->top = L1->top + LUA_MINSTACK;
+ L1->ci->top = L1->top + LUA_MINSTACK;
+ L1->fstack = NULL;
}
static void freestack (lua_State *L, lua_State *L1) {
+ struct lua_longjmp *pj, *pprev, *pnext;
luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo);
- luaM_freearray(L, L1->stack, L1->stacksize, TValue);
+ luaM_freearray(L, L1->stack, L1->stacksize, TValue);
+
+ /* free try-catch info */
+ pj = L->errorJmp;
+ pnext = NULL;
+ while (pj) {
+ pprev = pj->previous;
+ if (pj->type == JMPTYPE_TRY) {
+ if (pnext == NULL)
+ L->errorJmp = pprev;
+ else
+ pnext->previous = pprev;
+ luaM_free(L, pj);
+ }
+ else
+ pnext = pj;
+ pj = pprev;
+ }
+
+ luaD_freefstack(L);
}
diff -urN src2/lstate.h src/lstate.h
--- src2/lstate.h 2008-01-03 23:20:40.000000000 +0800
+++ src/lstate.h 2008-02-01 10:09:05.696475200 +0800
@@ -51,7 +51,10 @@
StkId top; /* top for this function */
const Instruction *savedpc;
int nresults; /* expected number of results from this function */
- int tailcalls; /* number of tail calls lost under this entry */
+ int tailcalls; /* number of tail calls lost under this entry */
+
+ int numres; /* number or results, used by try */
+ ptrdiff_t baseres; /* base for result values */
} CallInfo;
@@ -123,7 +126,8 @@
GCObject *openupval; /* list of open upvalues in this stack */
GCObject *gclist;
struct lua_longjmp *errorJmp; /* current error recover point */
- ptrdiff_t errfunc; /* current error handling function (stack index) */
+ ptrdiff_t errfunc; /* current error handling function (stack index) */
+ struct lua_longjmp *fstack; /* stack for "finally" ret */
};
diff -urN src2/luaconf.h src/luaconf.h
--- src2/luaconf.h 2008-01-19 01:07:48.000000000 +0800
+++ src/luaconf.h 2008-01-27 10:31:25.827176000 +0800
@@ -603,14 +603,7 @@
** compiling as C++ code, with _longjmp/_setjmp when asked to use them,
** and with longjmp/setjmp otherwise.
*/
-#if defined(__cplusplus)
-/* C++ exceptions */
-#define LUAI_THROW(L,c) throw(c)
-#define LUAI_TRY(L,c,a) try { a } catch(...) \
- { if ((c)->status == 0) (c)->status = -1; }
-#define luai_jmpbuf int /* dummy variable */
-
-#elif defined(LUA_USE_ULONGJMP)
+#if defined(LUA_USE_ULONGJMP)
/* in Unix, try _longjmp/_setjmp (more efficient) */
#define LUAI_THROW(L,c) _longjmp((c)->b, 1)
#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a }
diff -urN src2/lvm.c src/lvm.c
--- src2/lvm.c 2007-12-28 23:32:24.000000000 +0800
+++ src/lvm.c 2008-02-01 10:50:38.380779200 +0800
@@ -368,13 +368,60 @@
Protect(Arith(L, ra, rb, rc, tm)); \
}
-
+
+static void releasetry(lua_State *L) {
+ struct lua_longjmp *pj = L->errorJmp;
+ if (pj->type == JMPTYPE_TRY) {
+ L->errfunc = pj->old_errfunc;
+ L->errorJmp = pj->previous;
+ luaM_free(L, pj);
+ }
+}
+
+static void restoretry(lua_State *L) {
+ struct lua_longjmp *pj = L->errorJmp;
+ StkId oldtop = restorestack(L, pj->old_top);
+ luaF_close(L, oldtop); /* close eventual pending closures */
+
+ L->nCcalls = pj->oldnCcalls;
+ L->ci = restoreci(L, pj->old_ci);
+ L->base = L->ci->base;
+ L->allowhook = pj->old_allowhooks;
+
+ if (pj->opcode != OP_TRY)
+ luaD_seterrorobj(L, pj->status, L->base + pj->errobj);
+ L->top = oldtop;
+
+ if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */
+ int inuse = cast_int(L->ci - L->base_ci);
+ if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */
+ luaD_reallocCI(L, LUAI_MAXCALLS);
+ }
+
+ if (pj->opcode == OP_TRYFIN) {
+ L->errorJmp = pj->previous;
+ pj->previous = L->fstack;
+ L->fstack = pj;
+ }
+ else
+ releasetry(L);
+}
+
+static void releasefin(lua_State *L) {
+ if (L->fstack) {
+ struct lua_longjmp *pj = L->fstack;
+ L->fstack = pj->previous;
+ luaM_free(L, pj);
+ }
+}
void luaV_execute (lua_State *L, int nexeccalls) {
LClosure *cl;
StkId base;
TValue *k;
- const Instruction *pc;
+ const Instruction *pc;
+ struct lua_longjmp *pj;
+
reentry: /* entry point */
lua_assert(isLua(L->ci));
pc = L->savedpc;
@@ -633,7 +680,11 @@
}
}
case OP_RETURN: {
- int b = GETARG_B(i);
+ int b = GETARG_B(i);
+ if (GETARG_C(i)) {
+ b = L->ci->numres + 1;
+ ra = restorestack(L, L->ci->baseres);
+ }
if (b != 0) L->top = ra+b-1;
if (L->openupval) luaF_close(L, base);
L->savedpc = pc;
@@ -756,7 +807,91 @@
}
}
continue;
- }
+ }
+ case OP_TRY:
+ case OP_TRYCATCH:
+ case OP_TRYFIN: {
+ pj = luaM_malloc(L, sizeof(struct lua_longjmp));
+ pj->type = JMPTYPE_TRY;
+ pj->status = 0;
+ pj->dest = pc + GETARG_sBx(i);
+ pj->previous = L->errorJmp;
+ pj->errobj = ra - base;
+ pj->opcode = GET_OPCODE(i);
+
+ pj->oldnCcalls = L->nCcalls;
+ pj->old_ci = saveci(L, L->ci);
+ pj->old_allowhooks = L->allowhook;
+ pj->old_errfunc = L->errfunc;
+ pj->old_nexeccalls = nexeccalls;
+ pj->old_top = savestack(L, L->top);
+ L->errorJmp = pj;
+ L->errfunc = 0;
+
+ if (setjmp(pj->b)) {
+ pc = L->errorJmp->dest;
+ nexeccalls = L->errorJmp->old_nexeccalls;
+ restoretry(L);
+ L->savedpc = pc;
+ goto reentry;
+ }
+
+ continue;
+ }
+ case OP_EXITTRY: {
+ if (L->errorJmp) {
+ struct lua_longjmp *pj = L->errorJmp;
+ int count = 0;
+ if (GETARG_C(i)) { /* save return values */
+ StkId res, t = L->top; /* top of return values */
+ TValue val;
+ int b = GETARG_B(i);
+
+ if (b != 0) t = ra+b-1;
+ if (L->openupval) luaF_close(L, base);
+
+ res = L->base;
+ for (res = L->base; ra < t; res ++, ra ++, count ++) {
+ StkId mp; val = *ra;
+ for (mp = ra; mp > res; mp --)
+ setobjs2s(L, mp, mp-1);
+ *res = val;
+ }
+ L->ci->baseres = savestack(L, L->base);
+ L->ci->numres = count;
+ L->ci->base = (L->base += count);
+ }
+
+ if (pj->opcode == OP_TRYFIN) { /* move it to fstack, and jump to 'finally' clause */
+ const Instruction *tmpc = pc;
+ setnilvalue(L->base + pj->errobj); /* clear internal error object */
+ L->errorJmp = pj->previous;
+ pc = pj->dest;
+ pj->dest = tmpc; /* save pc for RETFIN */
+ pj->previous = L->fstack;
+ L->fstack = pj;
+ } else
+ releasetry(L);
+
+ if (count) { /* base changed, so reenter */
+ L->savedpc = pc;
+ goto reentry;
+ }
+ }
+ continue;
+ }
+ case OP_RETFIN: {
+ if (!ttisnil(ra)) { /* raise error again */
+ int status = L->fstack->status;
+ releasefin(L);
+ L->top = ra + 1;
+ luaD_throw(L, status);
+ } else {
+ pc = L->fstack->dest;
+ releasefin(L);
+ }
+ continue;
+ }
}
}
}