diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-28 11:40:30 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2020-12-28 11:40:30 -0300 |
commit | 7af27ef59da4051914d93d8b63efac663b64765a (patch) | |
tree | 73ac919879b442904112dbb972412fc15983d50e | |
parent | 0ceada8da92135717d31a3954b5b89a954f9e71a (diff) | |
download | lua-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.c | 2 | ||||
-rw-r--r-- | ldo.c | 59 | ||||
-rw-r--r-- | ldo.h | 1 | ||||
-rw-r--r-- | lfunc.c | 75 | ||||
-rw-r--r-- | lfunc.h | 6 | ||||
-rw-r--r-- | lstate.c | 10 | ||||
-rw-r--r-- | lvm.c | 2 |
7 files changed, 80 insertions, 75 deletions
@@ -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 | } |
@@ -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 | */ |
638 | static int recover (lua_State *L, int status) { | 639 | static 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 | */ | ||
773 | struct CloseP { | ||
774 | StkId level; | ||
775 | int status; | ||
776 | }; | ||
777 | |||
778 | |||
779 | /* | ||
780 | ** Auxiliary function to call 'luaF_close' in protected mode. | ||
781 | */ | ||
782 | static 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 | */ | ||
792 | int 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; |
@@ -63,6 +63,7 @@ LUAI_FUNC CallInfo *luaD_precall (lua_State *L, StkId func, int nResults); | |||
63 | LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); | 63 | LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); |
64 | LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); | 64 | LUAI_FUNC void luaD_callnoyield (lua_State *L, StkId func, int nResults); |
65 | LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); | 65 | LUAI_FUNC void luaD_tryfuncTM (lua_State *L, StkId func); |
66 | LUAI_FUNC int luaD_closeprotected (lua_State *L, ptrdiff_t level, int status); | ||
66 | LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, | 67 | LUAI_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); |
68 | LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); | 69 | LUAI_FUNC void luaD_poscall (lua_State *L, CallInfo *ci, int nres); |
@@ -100,12 +100,6 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { | |||
100 | } | 100 | } |
101 | 101 | ||
102 | 102 | ||
103 | static 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 | */ |
150 | static int callclosemth (lua_State *L, StkId level, int status) { | 139 | static 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 | */ |
232 | int luaF_close (lua_State *L, StkId level, int status) { | 203 | void 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 | ||
@@ -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 | ||
56 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); | 56 | LUAI_FUNC Proto *luaF_newproto (lua_State *L); |
@@ -59,7 +59,7 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); | |||
59 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); | 59 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); |
60 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); | 60 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); |
61 | LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); | 61 | LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); |
62 | LUAI_FUNC int luaF_close (lua_State *L, StkId level, int status); | 62 | LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status); |
63 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); | 63 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); |
64 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); | 64 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); |
65 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, | 65 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, |
@@ -268,7 +268,7 @@ static void preinit_thread (lua_State *L, global_State *g) { | |||
268 | 268 | ||
269 | static void close_state (lua_State *L) { | 269 | static 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; |
@@ -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 | } |