From 435f587ed05e2c4d542e1db9ae9e4efbb7e02305 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Fri, 4 Aug 2000 16:38:35 -0300 Subject: [PATCH] (much) better handling of memory alloction errors --- lbuiltin.c | 8 ++--- lcode.c | 6 ++-- ldo.c | 94 ++++++++++++++++++++++++++++++------------------------ lmem.c | 14 ++++++-- lstate.c | 39 ++++++++++++++-------- lstring.c | 6 ++-- ltable.c | 10 +++--- ltests.c | 34 ++++++++++++++------ ltm.c | 6 ++-- lua.c | 8 +++-- 10 files changed, 137 insertions(+), 88 deletions(-) diff --git a/lbuiltin.c b/lbuiltin.c index 19524a25..dd479e94 100644 --- a/lbuiltin.c +++ b/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 */ diff --git a/lcode.c b/lcode.c index c80ea0da..fd7c6683 100644 --- a/lcode.c +++ b/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 */ diff --git a/ldo.c b/ldo.c index 92da2991..2d922078 100644 --- a/ldo.c +++ b/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; diff --git a/lmem.c b/lmem.c index f41d92d5..79a3e335 100644 --- a/lmem.c +++ b/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 +#include #include #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; } diff --git a/lstate.c b/lstate.c index 34c86bb1..2d7de542 100644 --- a/lstate.c +++ b/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!"); diff --git a/lstring.c b/lstring.c index ffc834b8..f70e967e 100644 --- a/lstring.c +++ b/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; } diff --git a/ltable.c b/ltable.c index 0e271e8c..d91ed36c 100644 --- a/ltable.c +++ b/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; ival) != TAG_NIL) diff --git a/ltests.c b/ltests.c index 9aef6953..40ba3f4b 100644 --- a/ltests.c +++ b/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") { diff --git a/ltm.c b/ltm.c index c2bd04fc..27866c2c 100644 --- a/ltm.c +++ b/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; } diff --git a/lua.c b/lua.c index 3734f84e..a97530d6 100644 --- a/lua.c +++ b/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); }