lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Hi Everyone,
 
The attachment is my try-catch patch for lua 5.1.3.
 
some side-effect:
1) Can't use C++ try-catch here, only setjmp..longjmp works. so I changed luaconf.h
2) in ldo.c , lua_longjmp structure become a bit bulky and moved to ldo.h
3) some opcodes and keywords added to VM, may cause incompatibility in luac binary code.
 
It's still in early stage of development, just post here to show my ideas. BREAK and RETURN statement in try..catch block are supported. but I'm sure there's something I've missed. so any comments are always welcome.
 
Here's some scramble code for testing, feel free to change it and see what happens:
--------------------------------------------------------------------------------------------------
function test(a)
  print ("enter catch: ", a)
end
 
function foo()
  print "foo!"
  -- error(debug.traceback("error"), 1)
end
 
function foo2()
  try
  for i=1,4 do
      print("i=",i)
      if i == 3 then error("f!", 1) end
  end
  end
  print "break!"
end
 
function haha(s)
  try
    print "here!"
    foo2()
    error("error", 1)
    print "there!"
  catch err do
    test("where")
    collectgarbage("collect")
    print (type(err), err)
  end
  print ("ok!"..s)
end
 
haha "done"
--------------------------------------------------------------------------------------------------
 
I've tested it with MSVC, then made the patch file by cygwin. I'm not experienced with diff/patch, sorry if I got anything wrong!
 
Hu
 

 
diff -urN src2/ldo.c src/ldo.c
--- src2/ldo.c	2008-01-19 06:31:22.000000000 +0800
+++ src/ldo.c	2008-01-27 10:33:19.500630400 +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: {
@@ -109,7 +101,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-01-27 08:44:54.466851200 +0800
@@ -11,6 +11,27 @@
 #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_* */
+  Instruction *pc;
+  ptrdiff_t old_ci;
+  lu_byte old_allowhooks;
+  ptrdiff_t old_errfunc;
+  int old_top;
+  int old_nexeccalls;
+  unsigned short oldnCcalls;
+};
 
 
 #define luaD_checkstack(L,n)	\
diff -urN src2/llex.c src/llex.c
--- src2/llex.c	2007-12-27 21:02:26.000000000 +0800
+++ src/llex.c	2008-01-27 08:46:38.546510400 +0800
@@ -35,10 +35,10 @@
 
 /* ORDER RESERVED */
 const char *const luaX_tokens [] = {
-    "and", "break", "do", "else", "elseif",
+    "and", "break", "catch", "do", "else", "elseif",
     "end", "false", "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-27 08:47:19.905982400 +0800
@@ -23,10 +23,10 @@
 */
 enum RESERVED {
   /* terminal symbols denoted by reserved words */
-  TK_AND = FIRST_RESERVED, TK_BREAK,
+  TK_AND = FIRST_RESERVED, TK_BREAK, TK_CATCH,
   TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, 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-27 10:35:14.536043200 +0800
@@ -36,7 +36,10 @@
   "NOT",
   "LEN",
   "CONCAT",
-  "JMP",
+  "JMP",
+  "TRY",
+  "ENDTRY",
+  "CATCH",
   "EQ",
   "LT",
   "LE",
@@ -83,6 +86,9 @@
  ,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, 0, OpArgN, OpArgN, iABC)		/* OP_ENDTRY */
+ ,opmode(0, 1, OpArgN, OpArgN, iABC)		/* OP_CATCH */
  ,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-27 10:34:43.581532800 +0800
@@ -181,6 +181,9 @@
 OP_CONCAT,/*	A B C	R(A) := R(B).. ... ..R(C)			*/
 
 OP_JMP,/*	sBx	pc+=sBx					*/
+OP_TRY,/* 	sBx	pc+=sBx					*/
+OP_ENDTRY,
+OP_CATCH, /* A  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++  		*/
diff -urN src2/lparser.c src/lparser.c
--- src2/lparser.c	2007-12-28 23:32:24.000000000 +0800
+++ src/lparser.c	2008-01-27 08:58:47.715003200 +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-catch */
 } 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:
       return 1;
     default: return 0;
   }
@@ -976,7 +977,9 @@
   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_ENDTRY, 0, 0, 0);
     upval |= bl->upval;
     bl = bl->previous;
   }
@@ -1161,6 +1164,59 @@
   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 escapelist = NO_JUMP;
+  int jpc = fs->jpc;  /* save list of jumps to here */
+  int pc;
+
+  fs->jpc = NO_JUMP;
+  luaX_next(ls);
+  pc = luaK_codeAsBx(fs, OP_TRY, 0, NO_JUMP);
+  luaK_concat(fs, &pc, jpc);  /* keep them on hold */
+
+  enterblock(fs, &bl, 2);   /* try-catch block */
+  block(ls);
+  leaveblock(fs);
+
+  if (ls->t.token == TK_CATCH) {
+    TString *varname;
+    int base;
+
+    luaK_codeABC(fs, OP_ENDTRY, 0, 0, 0);
+    luaK_concat(fs, &escapelist, luaK_jump(fs));
+    luaK_patchtohere(fs, pc);
+
+    // local err
+    enterblock(fs, &bl, 0);
+    luaX_next(ls);  /* skip `catch' */
+    varname = str_checkname(ls);  /* first variable name */
+
+    // do
+    checknext(ls, TK_DO);
+    base = fs->freereg;
+    new_localvar(ls, varname, 0);
+    adjustlocalvars(ls, 1);  /* control variables */
+    luaK_reserveregs(fs, 1);
+
+    luaK_codeABC(fs, OP_CATCH, base, 0, 0);  /* OP_CATCH sets error object to local 'varname'*/
+
+    block(ls);
+    leaveblock(fs);  /* loop scope (`break' jumps to this point) */
+  }
+  else {
+    luaK_codeABC(fs, OP_ENDTRY, 0, 0, 0);
+    luaK_concat(fs, &escapelist, pc);
+  }
+
+  luaK_patchtohere(fs, escapelist);
+
+  check_match(ls, TK_END, TK_TRY, line);
+}
+
 
 static void localfunc (LexState *ls) {
   expdesc v, b;
@@ -1238,6 +1294,7 @@
 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 */
   luaX_next(ls);  /* skip RETURN */
@@ -1263,6 +1320,13 @@
         lua_assert(nret == fs->freereg - first);
       }
     }
+  }
+
+  /* before return, we should exit all try-catch blocks */
+  while (bl) {
+    if (bl->isbreakable == 2)
+      luaK_codeABC(fs, OP_ENDTRY, 0, 0, 0);
+    bl = bl->previous;
   }
   luaK_ret(fs, first, nret);
 }
@@ -1296,6 +1360,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-01-27 10:30:58.978569600 +0800
@@ -59,8 +59,26 @@
 
 
 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;
+  }
 }
 
 
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-01-27 10:32:41.045334400 +0800
@@ -368,13 +368,47 @@
           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, int seterr, int ra) {
+  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 (seterr)
+    luaD_seterrorobj(L, pj->status, L->base + ra);
+  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);
+  }
+  releasetry(L);
+}
 
 
 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;
@@ -756,7 +790,43 @@
           }
         }
         continue;
-      }
+      }
+      case OP_TRY: {
+          int status;
+          pj = luaM_malloc(L, sizeof(struct lua_longjmp));
+          pj->type = JMPTYPE_TRY;
+          pj->status = 0;
+          pj->pc = pc + GETARG_sBx(i);
+          pj->previous = L->errorJmp;
+
+          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;
+
+          status = setjmp(pj->b);
+          if (status) {
+            pc = L->errorJmp->pc;
+            nexeccalls = L->errorJmp->old_nexeccalls;
+            restoretry(L, GET_OPCODE(*pc) == OP_CATCH, GETARG_A(*pc));
+            L->savedpc = pc;
+            goto reentry;
+          }
+
+        continue;
+      }
+      case OP_ENDTRY: {
+        releasetry(L);
+        continue;
+      }
+      case OP_CATCH: {
+        // dummy opcode, do nothing here!
+        continue;
+      }
     }
   }
 }