Преглед изворни кода

(much) better handling of memory alloction errors

Roberto Ierusalimschy пре 25 година
родитељ
комит
435f587ed0
10 измењених фајлова са 137 додато и 88 уклоњено
  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);
     }