aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-28 11:40:30 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-28 11:40:30 -0300
commit7af27ef59da4051914d93d8b63efac663b64765a (patch)
tree73ac919879b442904112dbb972412fc15983d50e
parent0ceada8da92135717d31a3954b5b89a954f9e71a (diff)
downloadlua-7af27ef59da4051914d93d8b63efac663b64765a.tar.gz
lua-7af27ef59da4051914d93d8b63efac663b64765a.tar.bz2
lua-7af27ef59da4051914d93d8b63efac663b64765a.zip
Cleaner handling of errors in '__close' metamethods
Instead of protecting each individual metamethod call, protect the entire call to 'luaF_close'.
-rw-r--r--lapi.c2
-rw-r--r--ldo.c59
-rw-r--r--ldo.h1
-rw-r--r--lfunc.c75
-rw-r--r--lfunc.h6
-rw-r--r--lstate.c10
-rw-r--r--lvm.c2
7 files changed, 80 insertions, 75 deletions
diff --git a/lapi.c b/lapi.c
index 03e756d6..00e95a11 100644
--- a/lapi.c
+++ b/lapi.c
@@ -188,7 +188,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
188 diff = idx + 1; /* will "subtract" index (as it is negative) */ 188 diff = idx + 1; /* will "subtract" index (as it is negative) */
189 } 189 }
190 if (diff < 0 && hastocloseCfunc(ci->nresults)) 190 if (diff < 0 && hastocloseCfunc(ci->nresults))
191 luaF_close(L, L->top + diff, LUA_OK); 191 luaF_close(L, L->top + diff, CLOSEKTOP);
192 L->top += diff; /* correct top only after closing any upvalue */ 192 L->top += diff; /* correct top only after closing any upvalue */
193 lua_unlock(L); 193 lua_unlock(L);
194} 194}
diff --git a/ldo.c b/ldo.c
index 4b55c31c..59391f7b 100644
--- a/ldo.c
+++ b/ldo.c
@@ -98,11 +98,12 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
98 setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); 98 setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
99 break; 99 break;
100 } 100 }
101 case CLOSEPROTECT: { 101 case LUA_OK: { /* special case only for closing upvalues */
102 setnilvalue(s2v(oldtop)); /* no error message */ 102 setnilvalue(s2v(oldtop)); /* no error message */
103 break; 103 break;
104 } 104 }
105 default: { 105 default: {
106 lua_assert(errcode >= LUA_ERRRUN); /* real error */
106 setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ 107 setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
107 break; 108 break;
108 } 109 }
@@ -118,7 +119,7 @@ l_noret luaD_throw (lua_State *L, int errcode) {
118 } 119 }
119 else { /* thread has no error handler */ 120 else { /* thread has no error handler */
120 global_State *g = G(L); 121 global_State *g = G(L);
121 errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */ 122 errcode = luaD_closeprotected(L, 0, errcode); /* close all upvalues */
122 L->status = cast_byte(errcode); /* mark it as dead */ 123 L->status = cast_byte(errcode); /* mark it as dead */
123 if (g->mainthread->errorJmp) { /* main thread has a handler? */ 124 if (g->mainthread->errorJmp) { /* main thread has a handler? */
124 setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */ 125 setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
@@ -409,7 +410,7 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
409 default: /* multiple results (or to-be-closed variables) */ 410 default: /* multiple results (or to-be-closed variables) */
410 if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ 411 if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
411 ptrdiff_t savedres = savestack(L, res); 412 ptrdiff_t savedres = savestack(L, res);
412 luaF_close(L, res, LUA_OK); /* may change the stack */ 413 luaF_close(L, res, CLOSEKTOP); /* may change the stack */
413 res = restorestack(L, savedres); 414 res = restorestack(L, savedres);
414 wanted = codeNresults(wanted); /* correct value */ 415 wanted = codeNresults(wanted); /* correct value */
415 if (wanted == LUA_MULTRET) 416 if (wanted == LUA_MULTRET)
@@ -636,16 +637,13 @@ static CallInfo *findpcall (lua_State *L) {
636** 'luaD_pcall'. If there is no recover point, returns zero. 637** 'luaD_pcall'. If there is no recover point, returns zero.
637*/ 638*/
638static int recover (lua_State *L, int status) { 639static int recover (lua_State *L, int status) {
639 StkId oldtop;
640 CallInfo *ci = findpcall(L); 640 CallInfo *ci = findpcall(L);
641 if (ci == NULL) return 0; /* no recovery point */ 641 if (ci == NULL) return 0; /* no recovery point */
642 /* "finish" luaD_pcall */ 642 /* "finish" luaD_pcall */
643 oldtop = restorestack(L, ci->u2.funcidx);
644 L->ci = ci; 643 L->ci = ci;
645 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ 644 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
646 status = luaF_close(L, oldtop, status); /* may change the stack */ 645 status = luaD_closeprotected(L, ci->u2.funcidx, status);
647 oldtop = restorestack(L, ci->u2.funcidx); 646 luaD_seterrorobj(L, status, restorestack(L, ci->u2.funcidx));
648 luaD_seterrorobj(L, status, oldtop);
649 luaD_shrinkstack(L); /* restore stack size in case of overflow */ 647 luaD_shrinkstack(L); /* restore stack size in case of overflow */
650 L->errfunc = ci->u.c.old_errfunc; 648 L->errfunc = ci->u.c.old_errfunc;
651 return 1; /* continue running the coroutine */ 649 return 1; /* continue running the coroutine */
@@ -770,6 +768,45 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx,
770 768
771 769
772/* 770/*
771** Auxiliary structure to call 'luaF_close' in protected mode.
772*/
773struct CloseP {
774 StkId level;
775 int status;
776};
777
778
779/*
780** Auxiliary function to call 'luaF_close' in protected mode.
781*/
782static void closepaux (lua_State *L, void *ud) {
783 struct CloseP *pcl = cast(struct CloseP *, ud);
784 luaF_close(L, pcl->level, pcl->status);
785}
786
787
788/*
789** Calls 'luaF_close' in protected mode. Return the original status
790** or, in case of errors, the new status.
791*/
792int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status) {
793 CallInfo *old_ci = L->ci;
794 lu_byte old_allowhooks = L->allowhook;
795 for (;;) { /* keep closing upvalues until no more errors */
796 struct CloseP pcl;
797 pcl.level = restorestack(L, level); pcl.status = status;
798 status = luaD_rawrunprotected(L, &closepaux, &pcl);
799 if (likely(status == LUA_OK)) /* no more errors? */
800 return pcl.status;
801 else { /* an error occurred; restore saved state and repeat */
802 L->ci = old_ci;
803 L->allowhook = old_allowhooks;
804 }
805 }
806}
807
808
809/*
773** Call the C function 'func' in protected mode, restoring basic 810** Call the C function 'func' in protected mode, restoring basic
774** thread information ('allowhook', etc.) and in particular 811** thread information ('allowhook', etc.) and in particular
775** its stack level in case of errors. 812** its stack level in case of errors.
@@ -783,12 +820,10 @@ int luaD_pcall (lua_State *L, Pfunc func, void *u,
783 L->errfunc = ef; 820 L->errfunc = ef;
784 status = luaD_rawrunprotected(L, func, u); 821 status = luaD_rawrunprotected(L, func, u);
785 if (unlikely(status != LUA_OK)) { /* an error occurred? */ 822 if (unlikely(status != LUA_OK)) { /* an error occurred? */
786 StkId oldtop = restorestack(L, old_top);
787 L->ci = old_ci; 823 L->ci = old_ci;
788 L->allowhook = old_allowhooks; 824 L->allowhook = old_allowhooks;
789 status = luaF_close(L, oldtop, status); 825 status = luaD_closeprotected(L, old_top, status);
790 oldtop = restorestack(L, old_top); /* previous call may change stack */ 826 luaD_seterrorobj(L, status, restorestack(L, old_top));
791 luaD_seterrorobj(L, status, oldtop);
792 luaD_shrinkstack(L); /* restore stack size in case of overflow */ 827 luaD_shrinkstack(L); /* restore stack size in case of overflow */
793 } 828 }
794 L->errfunc = old_errfunc; 829 L->errfunc = old_errfunc;
diff --git a/ldo.h b/ldo.h
index 4d30d072..c7721d62 100644
--- a/ldo.h
+++ b/ldo.h
@@ -63,6 +63,7 @@ LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults);
63LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); 63LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults);
64LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); 64LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults);
65LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); 65LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func);
66LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status);
66LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, 67LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u,
67 ptrdiff_t oldtop, ptrdiff_t ef); 68 ptrdiff_t oldtop, ptrdiff_t ef);
68LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); 69LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres);
diff --git a/lfunc.c b/lfunc.c
index bfbf270b..ae68487c 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -100,12 +100,6 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
100} 100}
101 101
102 102
103static void callclose (lua_State *L, void *ud) {
104 UNUSED(ud);
105 luaD_callnoyield(L, L->top - 3, 0);
106}
107
108
109/* 103/*
110** Prepare closing method plus its arguments for object 'obj' with 104** Prepare closing method plus its arguments for object 'obj' with
111** error message 'err'. (This function assumes EXTRA_STACK.) 105** error message 'err'. (This function assumes EXTRA_STACK.)
@@ -136,40 +130,25 @@ static void varerror (lua_State *L, StkId level, const char *msg) {
136 130
137 131
138/* 132/*
139** Prepare and call a closing method. If status is OK, code is still 133** Prepare and call a closing method.
140** inside the original protected call, and so any error will be handled 134** If status is CLOSEKTOP, the call to the closing method will be pushed
141** there. Otherwise, a previous error already activated the original 135** at the top of the stack. Otherwise, values can be pushed right after
142** protected call, and so the call to the closing method must be 136** the 'level' of the upvalue being closed, as everything after that
143** protected here. (A status == CLOSEPROTECT behaves like a previous 137** won't be used again.
144** error, to also run the closing method in protected mode).
145** If status is OK, the call to the closing method will be pushed
146** at the top of the stack. Otherwise, values are pushed after
147** the 'level' of the upvalue being closed, as everything after
148** that won't be used again.
149*/ 138*/
150static int callclosemth (lua_State *L, StkId level, int status) { 139static void callclosemth (lua_State *L, StkId level, int status) {
151 TValue *uv = s2v(level); /* value being closed */ 140 TValue *uv = s2v(level); /* value being closed */
152 if (likely(status == LUA_OK)) { 141 TValue *errobj;
153 if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ 142 if (status == CLOSEKTOP)
154 callclose(L, NULL); /* call closing method */ 143 errobj = &G(L)->nilvalue; /* error object is nil */
155 else if (!l_isfalse(uv)) /* non-closable non-false value? */ 144 else { /* 'luaD_seterrorobj' will set top to level + 2 */
156 varerror(L, level, "attempt to close non-closable variable '%s'"); 145 errobj = s2v(level + 1); /* error object goes after 'uv' */
157 } 146 luaD_seterrorobj(L, status, level + 1); /* set error object */
158 else { /* must close the object in protected mode */
159 ptrdiff_t oldtop;
160 level++; /* space for error message */
161 oldtop = savestack(L, level + 1); /* top will be after that */
162 luaD_seterrorobj(L, status, level); /* set error message */
163 if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
164 int newstatus = luaD_pcall(L, callclose, NULL, oldtop, 0);
165 if (newstatus != LUA_OK) /* new error? */
166 status = newstatus; /* this will be the error now */
167 else /* leave original error (or nil) on top */
168 L->top = restorestack(L, oldtop);
169 }
170 /* else no metamethod; ignore this case and keep original error */
171 } 147 }
172 return status; 148 if (prepclosingmethod(L, uv, errobj)) /* something to call? */
149 luaD_callnoyield(L, L->top - 3, 0); /* call method */
150 else if (!l_isfalse(uv)) /* non-closable non-false value? */
151 varerror(L, level, "attempt to close non-closable variable '%s'");
173} 152}
174 153
175 154
@@ -201,7 +180,7 @@ void luaF_newtbcupval (lua_State *L, StkId level) {
201 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ 180 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
202 /* next call must succeed, as object is closable */ 181 /* next call must succeed, as object is closable */
203 prepclosingmethod(L, s2v(level), s2v(level + 1)); 182 prepclosingmethod(L, s2v(level), s2v(level + 1));
204 callclose(L, NULL); /* call closing method */ 183 luaD_callnoyield(L, L->top - 3, 0); /* call method */
205 luaD_throw(L, LUA_ERRMEM); /* throw memory error */ 184 luaD_throw(L, LUA_ERRMEM); /* throw memory error */
206 } 185 }
207 } 186 }
@@ -217,19 +196,11 @@ void luaF_unlinkupval (UpVal *uv) {
217 196
218 197
219/* 198/*
220** Close all upvalues up to the given stack level. 'status' indicates 199** Close all upvalues up to the given stack level. A 'status' equal
221** how/why the function was called: 200** to NOCLOSINGMETH closes upvalues without running any __close
222** - LUA_OK: regular code exiting the scope of a variable; may raise 201** metamethods.
223** an error due to errors in __close metamethods;
224** - CLOSEPROTECT: finishing a thread; run all metamethods in protected
225** mode;
226** - NOCLOSINGMETH: close upvalues without running __close metamethods;
227** - other values: error status from previous errors, to be propagated.
228**
229** Returns the resulting status, either the original status or an error
230** in a closing method.
231*/ 202*/
232int luaF_close (lua_State *L, StkId level, int status) { 203void luaF_close (lua_State *L, StkId level, int status) {
233 UpVal *uv; 204 UpVal *uv;
234 StkId upl; /* stack index pointed by 'uv' */ 205 StkId upl; /* stack index pointed by 'uv' */
235 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { 206 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
@@ -243,13 +214,11 @@ int luaF_close (lua_State *L, StkId level, int status) {
243 luaC_barrier(L, uv, slot); 214 luaC_barrier(L, uv, slot);
244 } 215 }
245 if (uv->tbc && status != NOCLOSINGMETH) { 216 if (uv->tbc && status != NOCLOSINGMETH) {
246 /* must run closing method, which may change the stack */
247 ptrdiff_t levelrel = savestack(L, level); 217 ptrdiff_t levelrel = savestack(L, level);
248 status = callclosemth(L, upl, status); 218 callclosemth(L, upl, status); /* may change the stack */
249 level = restorestack(L, levelrel); 219 level = restorestack(L, levelrel);
250 } 220 }
251 } 221 }
252 return status;
253} 222}
254 223
255 224
diff --git a/lfunc.h b/lfunc.h
index 8d6f965c..40de4636 100644
--- a/lfunc.h
+++ b/lfunc.h
@@ -49,8 +49,8 @@
49/* close upvalues without running their closing methods */ 49/* close upvalues without running their closing methods */
50#define NOCLOSINGMETH (-1) 50#define NOCLOSINGMETH (-1)
51 51
52/* close upvalues running all closing methods in protected mode */ 52/* special status to close upvalues preserving the top of the stack */
53#define CLOSEPROTECT (-2) 53#define CLOSEKTOP (-2)
54 54
55 55
56LUAI_FUNC Proto *luaF_newproto (lua_State *L); 56LUAI_FUNC Proto *luaF_newproto (lua_State *L);
@@ -59,7 +59,7 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
59LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); 59LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
60LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); 60LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
61LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); 61LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
62LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status); 62LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status);
63LUAI_FUNC void luaF_unlinkupval (UpVal *uv); 63LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
64LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); 64LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
65LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, 65LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
diff --git a/lstate.c b/lstate.c
index 96187c66..e01a7e7b 100644
--- a/lstate.c
+++ b/lstate.c
@@ -268,7 +268,7 @@ static void preinit_thread (lua_State *L, global_State *g) {
268 268
269static void close_state (lua_State *L) { 269static void close_state (lua_State *L) {
270 global_State *g = G(L); 270 global_State *g = G(L);
271 luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues */ 271 luaD_closeprotected(L, 0, LUA_OK); /* close all upvalues */
272 luaC_freeallobjects(L); /* collect all objects */ 272 luaC_freeallobjects(L); /* collect all objects */
273 if (ttisnil(&g->nilvalue)) /* closing a fully built state? */ 273 if (ttisnil(&g->nilvalue)) /* closing a fully built state? */
274 luai_userstateclose(L); 274 luai_userstateclose(L);
@@ -329,10 +329,10 @@ int lua_resetthread (lua_State *L) {
329 setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */ 329 setnilvalue(s2v(L->stack)); /* 'function' entry for basic 'ci' */
330 ci->func = L->stack; 330 ci->func = L->stack;
331 ci->callstatus = CIST_C; 331 ci->callstatus = CIST_C;
332 if (status == LUA_OK || status == LUA_YIELD) 332 if (status == LUA_YIELD)
333 status = CLOSEPROTECT; /* run closing methods in protected mode */ 333 status = LUA_OK;
334 status = luaF_close(L, L->stack, status); 334 status = luaD_closeprotected(L, 0, status);
335 if (status != CLOSEPROTECT) /* errors? */ 335 if (status != LUA_OK) /* errors? */
336 luaD_seterrorobj(L, status, L->stack + 1); 336 luaD_seterrorobj(L, status, L->stack + 1);
337 else { 337 else {
338 status = LUA_OK; 338 status = LUA_OK;
diff --git a/lvm.c b/lvm.c
index ccebdbe0..a6f04606 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1662,7 +1662,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1662 if (TESTARG_k(i)) { /* may there be open upvalues? */ 1662 if (TESTARG_k(i)) { /* may there be open upvalues? */
1663 if (L->top < ci->top) 1663 if (L->top < ci->top)
1664 L->top = ci->top; 1664 L->top = ci->top;
1665 luaF_close(L, base, LUA_OK); 1665 luaF_close(L, base, CLOSEKTOP);
1666 updatetrap(ci); 1666 updatetrap(ci);
1667 updatestack(ci); 1667 updatestack(ci);
1668 } 1668 }