aboutsummaryrefslogtreecommitdiff
path: root/lfunc.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-25 12:50:20 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-10-25 12:50:20 -0300
commit41c800b352149e037bdebd5f20d2f25ed2a0e2a5 (patch)
tree740a459fd69d687dfe200fc91762208079e0c25b /lfunc.c
parent0a9aca56caa925c42aaa683b43560357ab736ea4 (diff)
downloadlua-41c800b352149e037bdebd5f20d2f25ed2a0e2a5.tar.gz
lua-41c800b352149e037bdebd5f20d2f25ed2a0e2a5.tar.bz2
lua-41c800b352149e037bdebd5f20d2f25ed2a0e2a5.zip
Closing methods should not interfere with returning values
A closing method cannot be called in its own stack slot, as there may be returning values in the stack after that slot, and the call would corrupt those values. Instead, the closing method must be copied to the top of the stack to be called. Moreover, even when a function returns no value, its return istruction still has to have its position (which will set the stack top) after the local variables, otherwise a closing method might corrupt another not-yet-called closing method.
Diffstat (limited to 'lfunc.c')
-rw-r--r--lfunc.c64
1 files changed, 33 insertions, 31 deletions
diff --git a/lfunc.c b/lfunc.c
index 4f9362f3..15874f88 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -98,27 +98,29 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
98 98
99 99
100static void callclose (lua_State *L, void *ud) { 100static void callclose (lua_State *L, void *ud) {
101 luaD_callnoyield(L, cast(StkId, ud), 0); 101 UNUSED(ud);
102 luaD_callnoyield(L, L->top - 2, 0);
102} 103}
103 104
104 105
105/* 106/*
106** Prepare closing method with its argument for object at 107** Prepare closing method plus its argument for object 'obj' with
107** index 'func' in the stack. Assume there is an error message 108** error message 'err'. (This function assumes EXTRA_STACK.)
108** (or nil) just below the object.
109*/ 109*/
110static int prepclosingmethod (lua_State *L, StkId func) { 110static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) {
111 if (ttisfunction(s2v(func))) { /* object to-be-closed is a function? */ 111 StkId top = L->top;
112 setobjs2s(L, func + 1, func - 1); /* push error msg. as argument */ 112 if (ttisfunction(obj)) { /* object to-be-closed is a function? */
113 setobj2s(L, top, obj); /* push function */
114 setobj2s(L, top + 1, err); /* push error msg. as argument */
113 } 115 }
114 else { /* try '__close' metamethod */ 116 else { /* try '__close' metamethod */
115 const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CLOSE); 117 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
116 if (ttisnil(tm)) /* no metamethod? */ 118 if (ttisnil(tm)) /* no metamethod? */
117 return 0; /* nothing to call */ 119 return 0; /* nothing to call */
118 setobjs2s(L, func + 1, func); /* 'self' is the argument */ 120 setobj2s(L, top, tm); /* will call metamethod... */
119 setobj2s(L, func, tm); /* will call metamethod */ 121 setobj2s(L, top + 1, obj); /* with 'self' as the argument */
120 } 122 }
121 L->top = func + 2; /* add function and argument */ 123 L->top = top + 2; /* add function and argument */
122 return 1; 124 return 1;
123} 125}
124 126
@@ -129,22 +131,24 @@ static int prepclosingmethod (lua_State *L, StkId func) {
129** will be handled there. Otherwise, a previous error already 131** will be handled there. Otherwise, a previous error already
130** activated original protected call, and so the call to the 132** activated original protected call, and so the call to the
131** closing method must be protected here. 133** closing method must be protected here.
134** If status is OK, the call to the closing method will be pushed
135** at the top of the stack. Otherwise, values are pushed after
136** the 'level' of the upvalue being closed, as everything after
137** that won't be used again.
132*/ 138*/
133static int closeupval (lua_State *L, TValue *uv, StkId level, int status) { 139static int closeupval (lua_State *L, TValue *uv, StkId level, int status) {
134 StkId func = level + 1; /* save slot for old error message */ 140 if (likely(status == LUA_OK)) {
135 if (unlikely(status != LUA_OK)) /* was there an error? */ 141 if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */
136 luaD_seterrorobj(L, status, level); /* save error message */ 142 callclose(L, NULL); /* call closing method */
137 else 143 }
138 setnilvalue(s2v(level)); /* no error message */ 144 else { /* there was an error */
139 setobj2s(L, func, uv); /* put object on top of error message */ 145 /* save error message and set stack top to 'level + 1' */
140 if (!prepclosingmethod(L, func)) 146 luaD_seterrorobj(L, status, level);
141 return status; /* nothing to call */ 147 if (prepclosingmethod(L, uv, s2v(level))) { /* something to call? */
142 if (likely(status == LUA_OK)) /* not in "error mode"? */ 148 int newstatus = luaD_pcall(L, callclose, NULL, savestack(L, level), 0);
143 callclose(L, func); /* call closing method */ 149 if (newstatus != LUA_OK) /* another error when closing? */
144 else { /* already inside error handler; cannot raise another error */ 150 status = newstatus; /* this will be the new error */
145 int newstatus = luaD_pcall(L, callclose, func, savestack(L, level), 0); 151 }
146 if (newstatus != LUA_OK) /* error when closing? */
147 status = newstatus; /* this will be the new error */
148 } 152 }
149 return status; 153 return status;
150} 154}
@@ -169,12 +173,10 @@ static void trynewtbcupval (lua_State *L, void *ud) {
169void luaF_newtbcupval (lua_State *L, StkId level) { 173void luaF_newtbcupval (lua_State *L, StkId level) {
170 int status = luaD_rawrunprotected(L, trynewtbcupval, level); 174 int status = luaD_rawrunprotected(L, trynewtbcupval, level);
171 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ 175 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
172 StkId func = level + 1;
173 lua_assert(status == LUA_ERRMEM); 176 lua_assert(status == LUA_ERRMEM);
174 setobjs2s(L, func, level); /* open space for error message */ 177 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
175 luaD_seterrorobj(L, status, level); /* save error message */ 178 if (prepclosingmethod(L, s2v(level), s2v(level + 1)))
176 if (prepclosingmethod(L, func)) 179 callclose(L, NULL); /* call closing method */
177 callclose(L, func); /* call closing method */
178 luaD_throw(L, LUA_ERRMEM); /* throw memory error */ 180 luaD_throw(L, LUA_ERRMEM); /* throw memory error */
179 } 181 }
180} 182}
@@ -201,7 +203,7 @@ int luaF_close (lua_State *L, StkId level, int status) {
201 luaC_barrier(L, uv, slot); 203 luaC_barrier(L, uv, slot);
202 if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */ 204 if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */
203 ptrdiff_t levelrel = savestack(L, level); 205 ptrdiff_t levelrel = savestack(L, level);
204 status = closeupval(L, uv->v, upl, status); /* may reallocate the stack */ 206 status = closeupval(L, uv->v, upl, status); /* may realloc. the stack */
205 level = restorestack(L, levelrel); 207 level = restorestack(L, levelrel);
206 } 208 }
207 } 209 }