Browse Source

(much) better handling of memory alloction errors

Roberto Ierusalimschy 25 năm trước cách đây
mục cha
commit
435f587ed0
10 tập tin đã thay đổi với 137 bổ sung88 xóa
  1. 3 5
      lbuiltin.c
  2. 3 3
      lcode.c
  3. 52 42
      ldo.c
  4. 11 3
      lmem.c
  5. 26 13
      lstate.c
  6. 3 3
      lstring.c
  7. 6 4
      ltable.c
  8. 24 10
      ltests.c
  9. 3 3
      ltm.c
  10. 6 2
      lua.c

+ 3 - 5
lbuiltin.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lbuiltin.c,v 1.116 2000/06/12 13:52:05 roberto Exp roberto $
+** $Id: lbuiltin.c,v 1.117 2000/06/30 14:35:17 roberto Exp roberto $
 ** Built-in functions
 ** See Copyright Notice in lua.h
 */
@@ -365,8 +365,8 @@ void luaB_tostring (lua_State *L) {
       sprintf(buff, "function: %p", clvalue(o));
       break;
     case TAG_USERDATA:
-      sprintf(buff, "userdata: %p(%d)", tsvalue(o)->u.d.value,
-                                        tsvalue(o)->u.d.tag);
+      sprintf(buff, "userdata(%d): %p", tsvalue(o)->u.d.tag,
+                                        tsvalue(o)->u.d.value);
       break;
     case TAG_NIL:
       lua_pushstring(L, "nil");
@@ -680,8 +680,6 @@ static const struct luaL_reg builtin_funcs[] = {
 
 
 void luaB_predefine (lua_State *L) {
-  /* pre-register mem error messages, to avoid loop when error arises */
-  luaS_newfixed(L, memEM);
   luaL_openl(L, builtin_funcs);
 #ifdef DEBUG
   luaB_opentests(L);  /* internal test functions */

+ 3 - 3
lcode.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lcode.c,v 1.40 2000/06/28 20:20:36 roberto Exp roberto $
+** $Id: lcode.c,v 1.41 2000/06/30 14:35:17 roberto Exp roberto $
 ** Code generator for Lua
 ** See Copyright Notice in lua.h
 */
@@ -620,8 +620,8 @@ int luaK_code2 (FuncState *fs, OpCode o, int arg1, int arg2) {
   }
   if (fs->debug) {
     LexState *ls = fs->ls;
-    luaX_checklimit(ls, ls->lastline, MAXARG_U, "lines in a chunk");
-    luaM_growvector(fs->L, fs->f->lines, fs->pc, 1, int, "??", MAXARG_U);
+    luaM_growvector(fs->L, fs->f->lines, fs->pc, 1, int,
+                    "code size overflow", MAX_INT);
     fs->f->lines[fs->pc] = ls->lastline;
   }
   /* put new instruction in code array */

+ 52 - 42
ldo.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ldo.c,v 1.80 2000/06/26 19:28:31 roberto Exp roberto $
+** $Id: ldo.c,v 1.81 2000/06/28 20:20:36 roberto Exp roberto $
 ** Stack and Call structure of Lua
 ** See Copyright Notice in lua.h
 */
@@ -237,17 +237,41 @@ static void message (lua_State *L, const char *s) {
   }
 }
 
+
+void luaD_breakrun (lua_State *L, int errcode) {
+  if (L->errorJmp) {
+    L->errorJmp->status = errcode;
+    longjmp(L->errorJmp->b, 1);
+  }
+  else {
+    if (errcode != LUA_ERRMEM)
+      message(L, "unable to recover; exiting\n");
+    exit(1);
+  }
+}
+
 /*
 ** Reports an error, and jumps up to the available recovery label
 */
 void lua_error (lua_State *L, const char *s) {
   if (s) message(L, s);
-  if (L->errorJmp)
-    longjmp(L->errorJmp->b, 1);
-  else {
-    message(L, "unable to recover; exiting\n");
-    exit(1);
-  }
+  luaD_breakrun(L, LUA_ERRRUN);
+}
+
+
+static void chain_longjmp (lua_State *L, struct lua_longjmp *lj) {
+  lj->base = L->Cstack.base;
+  lj->numCblocks = L->numCblocks;
+  lj->previous = L->errorJmp;
+  L->errorJmp = lj;
+}
+
+
+static void restore_longjmp (lua_State *L, struct lua_longjmp *lj) {
+  L->Cstack.num = 0;  /* no results */
+  L->top = L->Cstack.base = L->Cstack.lua2C = lj->base;
+  L->numCblocks = lj->numCblocks;
+  L->errorJmp = lj->previous;
 }
 
 
@@ -257,58 +281,44 @@ void lua_error (lua_State *L, const char *s) {
 */
 int luaD_protectedrun (lua_State *L) {
   struct lua_longjmp myErrorJmp;
-  StkId base = L->Cstack.base;
-  int numCblocks = L->numCblocks;
-  int status;
-  struct lua_longjmp *volatile oldErr = L->errorJmp;
-  L->errorJmp = &myErrorJmp;
+  chain_longjmp(L, &myErrorJmp);
   if (setjmp(myErrorJmp.b) == 0) {
+    StkId base = L->Cstack.base;
     luaD_call(L, base, MULT_RET);
     L->Cstack.lua2C = base;  /* position of the new results */
     L->Cstack.num = L->top - base;
     L->Cstack.base = base + L->Cstack.num;  /* incorporate results on stack */
-    status = 0;
+    L->errorJmp = myErrorJmp.previous;
+    return 0;
   }
   else {  /* an error occurred: restore the stack */
-    L->Cstack.num = 0;  /* no results */
-    L->top = L->Cstack.base = L->Cstack.lua2C = base;
-    L->numCblocks = numCblocks;
+    restore_longjmp(L, &myErrorJmp);
     restore_stack_limit(L);
-    status = 1;
+    return myErrorJmp.status;
   }
-  L->errorJmp = oldErr;
-  return status;
 }
 
 
 /*
-** returns 0 = chunk loaded; 1 = error; 2 = no more chunks to load
+** returns 0 = chunk loaded; >0 : error; -1 = no more chunks to load
 */
 static int protectedparser (lua_State *L, ZIO *z, int bin) {
   struct lua_longjmp myErrorJmp;
-  StkId base = L->Cstack.base;
-  int numCblocks = L->numCblocks;
-  int status;
-  Proto *volatile tf;
-  struct lua_longjmp *volatile oldErr = L->errorJmp;
-  L->errorJmp = &myErrorJmp;
-  L->top = base;   /* clear C2Lua */
+  chain_longjmp(L, &myErrorJmp);
+  L->top = L->Cstack.base;   /* clear C2Lua */
   if (setjmp(myErrorJmp.b) == 0) {
-    tf = bin ? luaU_undump1(L, z) : luaY_parser(L, z);
-    status = 0;
+    Proto *tf = bin ? luaU_undump1(L, z) : luaY_parser(L, z);
+    L->errorJmp = myErrorJmp.previous;
+    if (tf == NULL) return -1;  /* `natural' end */
+    luaV_Lclosure(L, tf, 0);
+    return 0;
   }
-  else {  /* an error occurred: restore Cstack and top */
-    L->Cstack.num = 0;  /* no results */
-    L->top = L->Cstack.base = L->Cstack.lua2C = base;
-    L->numCblocks = numCblocks;
-    tf = NULL;
-    status = 1;
+  else {  /* an error occurred */
+    restore_longjmp(L, &myErrorJmp);
+    if (myErrorJmp.status == LUA_ERRRUN)
+      myErrorJmp.status = LUA_ERRSYNTAX;
+    return myErrorJmp.status;  /* error code */
   }
-  L->errorJmp = oldErr;
-  if (status) return 1;  /* error code */
-  if (tf == NULL) return 2;  /* `natural' end */
-  luaV_Lclosure(L, tf, 0);
-  return 0;
 }
 
 
@@ -320,8 +330,8 @@ static int do_main (lua_State *L, ZIO *z, int bin) {
     luaC_checkGC(L);
     old_blocks = L->nblocks;
     status = protectedparser(L, z, bin);
-    if (status == 1) return 1;  /* error */
-    else if (status == 2) return 0;  /* `natural' end */
+    if (status > 0) return status;  /* error */
+    else if (status < 0) return 0;  /* `natural' end */
     else {
       unsigned long newelems2 = 2*(L->nblocks-old_blocks);
       L->GCthreshold += newelems2;

+ 11 - 3
lmem.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lmem.c,v 1.33 2000/06/12 13:52:05 roberto Exp roberto $
+** $Id: lmem.c,v 1.34 2000/06/26 19:28:31 roberto Exp roberto $
 ** Interface to Memory Manager
 ** See Copyright Notice in lua.h
 */
@@ -11,6 +11,7 @@
 
 #include "lua.h"
 
+#include "ldo.h"
 #include "lmem.h"
 #include "lobject.h"
 #include "lstate.h"
@@ -36,6 +37,7 @@
 
 
 #include <assert.h>
+#include <limits.h>
 #include <string.h>
 
 #undef realloc
@@ -59,6 +61,7 @@ union L_U { double d; char *s; long l; };
 unsigned long memdebug_numblocks = 0;
 unsigned long memdebug_total = 0;
 unsigned long memdebug_maxmem = 0;
+unsigned long memdebug_memlimit = LONG_MAX;
 
 
 static void *checkblock (void *block) {
@@ -88,6 +91,8 @@ static void *debug_realloc (void *block, size_t size) {
     freeblock(block);
     return NULL;
   }
+  else if (memdebug_total+size > memdebug_memlimit)
+    return NULL;  /* to test memory allocation errors */
   else {
     size_t realsize = HEADER+size+MARKSIZE;
     char *newblock = (char *)(malloc)(realsize);  /* alloc a new block */
@@ -139,8 +144,11 @@ void *luaM_realloc (lua_State *L, void *block, lint32 size) {
   else if (size >= MAX_SIZET)
     lua_error(L, "memory allocation error: block too big");
   block = realloc(block, size);
-  if (block == NULL)
-    lua_error(L, memEM);
+  if (block == NULL) {
+    if (L)
+      luaD_breakrun(L, LUA_ERRMEM);  /* break run without error message */
+    else return NULL;  /* error before creating state! */
+  }
   return block;
 }
 

+ 26 - 13
lstate.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.c,v 1.28 2000/06/30 14:35:17 roberto Exp roberto $
+** $Id: lstate.c,v 1.29 2000/06/30 19:17:08 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -28,8 +28,14 @@ lua_State *lua_state = NULL;
 
 
 lua_State *lua_newstate (int stacksize, int put_builtin) {
+  struct lua_longjmp myErrorJmp;
   lua_State *L = luaM_new(NULL, lua_State);
-  L->errorJmp = NULL;
+  if (L == NULL) return NULL;  /* memory allocation error */
+  L->stack = NULL;
+  L->strt.size = L->udt.size = 0;
+  L->strt.nuse = L->udt.nuse = 0;
+  L->strt.hash = NULL;
+  L->udt.hash = NULL;
   L->Mbuffer = NULL;
   L->Mbuffbase = 0;
   L->Mbuffsize = 0;
@@ -40,6 +46,7 @@ lua_State *lua_newstate (int stacksize, int put_builtin) {
   L->rootcl = NULL;
   L->roottable = NULL;
   L->IMtable = NULL;
+  L->last_tag = -1;
   L->refArray = NULL;
   L->refSize = 0;
   L->refFree = NONEXT;
@@ -49,16 +56,23 @@ lua_State *lua_newstate (int stacksize, int put_builtin) {
   L->callhook = NULL;
   L->linehook = NULL;
   L->allowhooks = 1;
-  L->gt = luaH_new(L, 10);
-  if (stacksize == 0) stacksize = DEFAULT_STACK_SIZE;
-  luaD_init(L, stacksize);
-  luaS_init(L);
-  luaX_init(L);
-  luaT_init(L);
-  if (put_builtin)
-    luaB_predefine(L);
-  L->GCthreshold = L->nblocks*4;
-  return L;
+  L->errorJmp = &myErrorJmp;
+  if (setjmp(myErrorJmp.b) == 0) {  /* to catch memory allocation errors */
+    L->gt = luaH_new(L, 10);
+    luaD_init(L, (stacksize == 0) ? DEFAULT_STACK_SIZE : stacksize);
+    luaS_init(L);
+    luaX_init(L);
+    luaT_init(L);
+    if (put_builtin)
+      luaB_predefine(L);
+    L->GCthreshold = L->nblocks*4;
+    L->errorJmp = NULL;
+    return L;
+  }
+  else {  /* memory allocation error: free partial state */
+    lua_close(L);
+    return NULL;
+  }
 }
 
 
@@ -75,7 +89,6 @@ void lua_close (lua_State *L) {
   luaM_free(L, L->Cblocks);
   LUA_ASSERT(L->numCblocks == 0, "Cblocks still open");
   LUA_ASSERT(L->nblocks == 0, "wrong count for nblocks");
-  LUA_ASSERT(L->Cstack.base == L->top, "C2Lua not empty");
   luaM_free(L, L);
   if (L == lua_state) {
     LUA_ASSERT(memdebug_numblocks == 0, "memory leak!");

+ 3 - 3
lstring.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstring.c,v 1.39 2000/06/15 17:01:12 roberto Exp roberto $
+** $Id: lstring.c,v 1.40 2000/06/30 14:35:17 roberto Exp $
 ** String table (keeps all strings handled by Lua)
 ** See Copyright Notice in lua.h
 */
@@ -19,10 +19,10 @@
 
 
 void luaS_init (lua_State *L) {
-  L->strt.size = L->udt.size = 1;
-  L->strt.nuse = L->udt.nuse = 0;
   L->strt.hash = luaM_newvector(L, 1, TString *);
   L->udt.hash = luaM_newvector(L, 1, TString *);
+  L->strt.size = L->udt.size = 1;
+  L->strt.nuse = L->udt.nuse = 0;
   L->strt.hash[0] = L->udt.hash[0] = NULL;
 }
 

+ 6 - 4
ltable.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltable.c,v 1.49 2000/06/28 17:03:56 roberto Exp roberto $
+** $Id: ltable.c,v 1.50 2000/06/30 14:35:17 roberto Exp roberto $
 ** Lua tables (hash)
 ** See Copyright Notice in lua.h
 */
@@ -154,19 +154,22 @@ static void setnodevector (lua_State *L, Hash *t, lint32 size) {
     ttype(&t->node[i].key) = ttype(&t->node[i].val) = TAG_NIL;
     t->node[i].next = NULL;
   }
+  L->nblocks += gcsize(L, size) - gcsize(L, t->size);
   t->size = size;
   t->firstfree = &t->node[size-1];  /* first free position to be used */
-  L->nblocks += gcsize(L, size);
 }
 
 
 Hash *luaH_new (lua_State *L, int size) {
   Hash *t = luaM_new(L, Hash);
-  setnodevector(L, t, luaO_power2(size));
   t->htag = TagDefault;
   t->next = L->roottable;
   L->roottable = t;
   t->marked = 0;
+  t->size = 0;
+  L->nblocks += gcsize(L, 0);
+  t->node = NULL;
+  setnodevector(L, t, luaO_power2(size));
   return t;
 }
 
@@ -204,7 +207,6 @@ static void rehash (lua_State *L, Hash *t) {
     setnodevector(L, t, oldsize/2);
   else
     setnodevector(L, t, oldsize);
-  L->nblocks -= gcsize(L, oldsize);
   for (i=0; i<oldsize; i++) {
     Node *old = nold+i;
     if (ttype(&old->val) != TAG_NIL)

+ 24 - 10
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 1.28 2000/06/28 17:06:07 roberto Exp roberto $
+** $Id: ltests.c,v 1.29 2000/06/30 19:17:08 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -169,9 +169,14 @@ static void get_limits (void) {
 
 
 static void mem_query (void) {
-  lua_pushnumber(memdebug_total);
-  lua_pushnumber(memdebug_numblocks);
-  lua_pushnumber(memdebug_maxmem);
+  lua_Object arg = lua_getparam(1);
+  if (arg == LUA_NOOBJECT) {
+    lua_pushnumber(memdebug_total);
+    lua_pushnumber(memdebug_numblocks);
+    lua_pushnumber(memdebug_maxmem);
+  }
+  else
+    memdebug_memlimit = luaL_check_int(1);
 }
 
 
@@ -375,7 +380,10 @@ static void testC (void) {
     else if EQ("newstate") {
       int stacksize = getnum(&pc);
       lua_State *L1 = lua_newstate(stacksize, getnum(&pc));
-      lua_pushuserdata(L1);
+      if (L1)
+        lua_pushuserdata(L1);
+      else
+        lua_pushnil();
     }
     else if EQ("closestate") {
       (lua_close)((lua_State *)lua_getuserdata(reg[getreg(&pc)]));
@@ -385,14 +393,20 @@ static void testC (void) {
       lua_Object str = reg[getreg(&pc)];
       lua_State *L1;
       lua_Object temp;
-      int i;
+      int status;
       if (!lua_isuserdata(ol1) || !lua_isstring(str))
         lua_error("bad arguments for `doremote'");
       L1 = (lua_State *)lua_getuserdata(ol1);
-      (lua_dostring)(L1, lua_getstring(str));
-      i = 1;
-      while ((temp = (lua_getresult)(L1, i++)) != LUA_NOOBJECT)
-        lua_pushstring((lua_getstring)(L1, temp));
+      status = (lua_dostring)(L1, lua_getstring(str));
+      if (status != 0) {
+        lua_pushnil();
+        lua_pushnumber(status);
+      }
+      else {
+        int i = 1;
+        while ((temp = (lua_getresult)(L1, i++)) != LUA_NOOBJECT)
+          lua_pushstring((lua_getstring)(L1, temp));
+      }
     }
 #if LUA_DEPRECATETFUNCS
     else if EQ("rawsetglobal") {

+ 3 - 3
ltm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltm.c,v 1.42 2000/06/08 17:48:31 roberto Exp roberto $
+** $Id: ltm.c,v 1.43 2000/06/12 13:52:05 roberto Exp roberto $
 ** Tag methods
 ** See Copyright Notice in lua.h
 */
@@ -69,17 +69,17 @@ static void init_entry (lua_State *L, int tag) {
 
 void luaT_init (lua_State *L) {
   int t;
-  L->last_tag = NUM_TAGS-1;
   luaM_growvector(L, L->IMtable, 0, NUM_TAGS, struct IM, "", MAX_INT);
+  L->last_tag = NUM_TAGS-1;
   for (t=0; t<=L->last_tag; t++)
     init_entry(L, t);
 }
 
 
 int lua_newtag (lua_State *L) {
-  ++L->last_tag;
   luaM_growvector(L, L->IMtable, L->last_tag, 1, struct IM,
                   "tag table overflow", MAX_INT);
+  L->last_tag++;
   init_entry(L, L->last_tag);
   return L->last_tag;
 }

+ 6 - 2
lua.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lua.c,v 1.41 2000/06/19 13:15:15 roberto Exp roberto $
+** $Id: lua.c,v 1.42 2000/06/30 19:17:08 roberto Exp roberto $
 ** Lua stand-alone interpreter
 ** See Copyright Notice in lua.h
 */
@@ -62,6 +62,10 @@ static int ldo (int (*f)(lua_State *L, const char *), const char *name) {
   handler h = lreset();
   res = f(lua_state, name);  /* dostring | dofile */
   signal(SIGINT, h);  /* restore old action */
+  if (res == LUA_ERRMEM) {
+    /* Lua gives no message in such case, so lua.c provides one */
+    fprintf(stderr, "lua: memory allocation error\n");
+  }
   return res;
 }
 
@@ -121,7 +125,7 @@ static void l_getargs (void) {
 static void file_input (const char *argv) {
   int result = ldo(lua_dofile, argv);
   if (result) {
-    if (result == 2) {
+    if (result == LUA_ERRFILE) {
       fprintf(stderr, "lua: cannot execute file ");
       perror(argv);
     }