aboutsummaryrefslogtreecommitdiff
path: root/lfunc.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-02-09 14:00:05 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-02-09 14:00:05 -0300
commit4e47f81188d37e29027158b76271d02a781242e2 (patch)
treec360912d1901acf8371390cc1f716278e5d91bb4 /lfunc.c
parentc63e5d212bc5dec1b1c749e3f07b42cd83081826 (diff)
downloadlua-4e47f81188d37e29027158b76271d02a781242e2.tar.gz
lua-4e47f81188d37e29027158b76271d02a781242e2.tar.bz2
lua-4e47f81188d37e29027158b76271d02a781242e2.zip
New implementation for to-be-closed variables
To-be-closed variables are linked in their own list, embedded into the stack elements. (Due to alignment, this information does not change the size of the stack elements in most architectures.) This new list does not produce garbage and avoids memory errors when creating tbc variables.
Diffstat (limited to 'lfunc.c')
-rw-r--r--lfunc.c66
1 files changed, 34 insertions, 32 deletions
diff --git a/lfunc.c b/lfunc.c
index 105590fc..b4c04bd0 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -120,11 +120,11 @@ static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
120 120
121 121
122/* 122/*
123** Check whether 'obj' has a close metamethod and raise an error 123** Check whether object at given level has a close metamethod and raise
124** if not. 124** an error if not.
125*/ 125*/
126static void checkclosemth (lua_State *L, StkId level, const TValue *obj) { 126static void checkclosemth (lua_State *L, StkId level) {
127 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); 127 const TValue *tm = luaT_gettmbyobj(L, s2v(level), TM_CLOSE);
128 if (ttisnil(tm)) { /* no metamethod? */ 128 if (ttisnil(tm)) { /* no metamethod? */
129 int idx = cast_int(level - L->ci->func); /* variable index */ 129 int idx = cast_int(level - L->ci->func); /* variable index */
130 const char *vname = luaG_findlocal(L, L->ci, idx, NULL); 130 const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
@@ -155,20 +155,21 @@ static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
155 155
156 156
157/* 157/*
158** Create a to-be-closed upvalue. If there is a memory allocation error, 158** Insert a variable in the list of to-be-closed variables.
159** 'ptbc' keeps the object so it can be closed as soon as possible.
160** (Since memory errors have no handler, that will happen before any
161** stack reallocation.)
162*/ 159*/
163void luaF_newtbcupval (lua_State *L, StkId level) { 160void luaF_newtbcupval (lua_State *L, StkId level) {
164 TValue *obj = s2v(level); 161 lua_assert(level > L->tbclist);
165 lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); 162 if (l_isfalse(s2v(level)))
166 if (!l_isfalse(obj)) { /* false doesn't need to be closed */ 163 return; /* false doesn't need to be closed */
167 checkclosemth(L, level, obj); 164 checkclosemth(L, level); /* value must have a close method */
168 L->ptbc = level; /* in case of allocation error */ 165 while (level - L->tbclist > USHRT_MAX) { /* is delta too large? */
169 newupval(L, 1, level, &L->openupval); 166 L->tbclist += USHRT_MAX; /* create a dummy node at maximum delta */
170 L->ptbc = NULL; /* no errors */ 167 L->tbclist->tbclist.delta = USHRT_MAX;
168 L->tbclist->tbclist.isdummy = 1;
171 } 169 }
170 level->tbclist.delta = level - L->tbclist;
171 level->tbclist.isdummy = 0;
172 L->tbclist = level;
172} 173}
173 174
174 175
@@ -181,23 +182,11 @@ void luaF_unlinkupval (UpVal *uv) {
181 182
182 183
183/* 184/*
184** Close all upvalues up to the given stack level. A 'status' equal 185** Close all upvalues up to the given stack level.
185** to NOCLOSINGMETH closes upvalues without running any __close
186** metamethods. If there is a pending to-be-closed value, close
187** it before anything else.
188*/ 186*/
189void luaF_close (lua_State *L, StkId level, int status, int yy) { 187void luaF_closeupval (lua_State *L, StkId level) {
190 UpVal *uv; 188 UpVal *uv;
191 StkId upl; /* stack index pointed by 'uv' */ 189 StkId upl; /* stack index pointed by 'uv' */
192 if (unlikely(status == LUA_ERRMEM && L->ptbc != NULL)) {
193 ptrdiff_t levelrel = savestack(L, level);
194 upl = L->ptbc;
195 L->ptbc = NULL; /* remove from "list" before closing */
196 prepcallclosemth(L, upl, status, yy);
197 level = restorestack(L, levelrel);
198 }
199 else
200 lua_assert(L->ptbc == NULL); /* must be empty for other status */
201 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { 190 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
202 TValue *slot = &uv->u.value; /* new position for value */ 191 TValue *slot = &uv->u.value; /* new position for value */
203 lua_assert(uplevel(uv) < L->top); 192 lua_assert(uplevel(uv) < L->top);
@@ -208,9 +197,22 @@ void luaF_close (lua_State *L, StkId level, int status, int yy) {
208 nw2black(uv); /* closed upvalues cannot be gray */ 197 nw2black(uv); /* closed upvalues cannot be gray */
209 luaC_barrier(L, uv, slot); 198 luaC_barrier(L, uv, slot);
210 } 199 }
211 if (uv->tbc && status != NOCLOSINGMETH) { 200 }
212 ptrdiff_t levelrel = savestack(L, level); 201}
213 prepcallclosemth(L, upl, status, yy); /* may change the stack */ 202
203
204/*
205** Close all upvalues and to-be-closed variables up to the given stack
206** level.
207*/
208void luaF_close (lua_State *L, StkId level, int status, int yy) {
209 ptrdiff_t levelrel = savestack(L, level);
210 luaF_closeupval(L, level); /* first, close the upvalues */
211 while (L->tbclist >= level) { /* traverse tbc's down to that level */
212 StkId tbc = L->tbclist; /* get variable index */
213 L->tbclist -= tbc->tbclist.delta; /* remove it from list */
214 if (!tbc->tbclist.isdummy) { /* not a dummy entry? */
215 prepcallclosemth(L, tbc, status, yy); /* close variable */
214 level = restorestack(L, levelrel); 216 level = restorestack(L, levelrel);
215 } 217 }
216 } 218 }