diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-18 16:15:09 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-10-18 16:15:09 -0300 |
| commit | 3c7dc52909ce0688bdb20cacaf686413a79aaf48 (patch) | |
| tree | 15a55b38f747154a1ebf4cf7d7eb088fe98daedc /lfunc.c | |
| parent | bd96330d037660d9a1769c6c0d989f017e5f0278 (diff) | |
| download | lua-3c7dc52909ce0688bdb20cacaf686413a79aaf48.tar.gz lua-3c7dc52909ce0688bdb20cacaf686413a79aaf48.tar.bz2 lua-3c7dc52909ce0688bdb20cacaf686413a79aaf48.zip | |
Handling of memory errors when creating to-be-closed upvalues
Diffstat (limited to 'lfunc.c')
| -rw-r--r-- | lfunc.c | 126 |
1 files changed, 94 insertions, 32 deletions
| @@ -40,6 +40,7 @@ LClosure *luaF_newLclosure (lua_State *L, int n) { | |||
| 40 | return c; | 40 | return c; |
| 41 | } | 41 | } |
| 42 | 42 | ||
| 43 | |||
| 43 | /* | 44 | /* |
| 44 | ** fill a closure with new closed upvalues | 45 | ** fill a closure with new closed upvalues |
| 45 | */ | 46 | */ |
| @@ -56,31 +57,43 @@ void luaF_initupvals (lua_State *L, LClosure *cl) { | |||
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | 59 | ||
| 60 | /* | ||
| 61 | ** Create a new upvalue with the given tag at the given level, | ||
| 62 | ** and link it to the list of open upvalues of 'L' after entry 'prev'. | ||
| 63 | **/ | ||
| 64 | static UpVal *newupval (lua_State *L, int tag, StkId level, UpVal **prev) { | ||
| 65 | GCObject *o = luaC_newobj(L, tag, sizeof(UpVal)); | ||
| 66 | UpVal *uv = gco2upv(o); | ||
| 67 | UpVal *next = *prev; | ||
| 68 | uv->v = s2v(level); /* current value lives in the stack */ | ||
| 69 | uv->u.open.next = next; /* link it to list of open upvalues */ | ||
| 70 | uv->u.open.previous = prev; | ||
| 71 | if (next) | ||
| 72 | next->u.open.previous = &uv->u.open.next; | ||
| 73 | *prev = uv; | ||
| 74 | if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ | ||
| 75 | L->twups = G(L)->twups; /* link it to the list */ | ||
| 76 | G(L)->twups = L; | ||
| 77 | } | ||
| 78 | return uv; | ||
| 79 | } | ||
| 80 | |||
| 81 | |||
| 82 | /* | ||
| 83 | ** Find and reuse, or create if it does not exist, a regular upvalue | ||
| 84 | ** at the given level. | ||
| 85 | */ | ||
| 59 | UpVal *luaF_findupval (lua_State *L, StkId level) { | 86 | UpVal *luaF_findupval (lua_State *L, StkId level) { |
| 60 | UpVal **pp = &L->openupval; | 87 | UpVal **pp = &L->openupval; |
| 61 | GCObject *o; | ||
| 62 | UpVal *p; | 88 | UpVal *p; |
| 63 | UpVal *uv; | ||
| 64 | lua_assert(isintwups(L) || L->openupval == NULL); | 89 | lua_assert(isintwups(L) || L->openupval == NULL); |
| 65 | while ((p = *pp) != NULL && uplevel(p) >= level) { | 90 | while ((p = *pp) != NULL && uplevel(p) >= level) { /* search for it */ |
| 66 | if (uplevel(p) == level && !isdead(G(L), p)) /* corresponding upvalue? */ | 91 | if (uplevel(p) == level && !isdead(G(L), p)) /* corresponding upvalue? */ |
| 67 | return p; /* return it */ | 92 | return p; /* return it */ |
| 68 | pp = &p->u.open.next; | 93 | pp = &p->u.open.next; |
| 69 | } | 94 | } |
| 70 | /* not found: create a new upvalue between 'pp' and 'p' */ | 95 | /* not found: create a new upvalue after 'pp' */ |
| 71 | o = luaC_newobj(L, LUA_TUPVAL, sizeof(UpVal)); | 96 | return newupval(L, LUA_TUPVAL, level, pp); |
| 72 | uv = gco2upv(o); | ||
| 73 | uv->u.open.next = p; /* link it to list of open upvalues */ | ||
| 74 | uv->u.open.previous = pp; | ||
| 75 | if (p) | ||
| 76 | p->u.open.previous = &uv->u.open.next; | ||
| 77 | *pp = uv; | ||
| 78 | uv->v = s2v(level); /* current value lives in the stack */ | ||
| 79 | if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ | ||
| 80 | L->twups = G(L)->twups; /* link it to the list */ | ||
| 81 | G(L)->twups = L; | ||
| 82 | } | ||
| 83 | return uv; | ||
| 84 | } | 97 | } |
| 85 | 98 | ||
| 86 | 99 | ||
| @@ -89,25 +102,44 @@ static void callclose (lua_State *L, void *ud) { | |||
| 89 | } | 102 | } |
| 90 | 103 | ||
| 91 | 104 | ||
| 92 | static int closeupval (lua_State *L, UpVal *uv, StkId level, int status) { | 105 | /* |
| 93 | StkId func = level + 1; /* save slot for old error message */ | 106 | ** Prepare closing method with its argument for object at |
| 94 | if (status != LUA_OK) /* was there an error? */ | 107 | ** index 'func' in the stack. Assume there is an error message |
| 95 | luaD_seterrorobj(L, status, level); /* save error message */ | 108 | ** (or nil) just below the object. |
| 96 | else | 109 | */ |
| 97 | setnilvalue(s2v(level)); | 110 | static int prepclosingmethod (lua_State *L, StkId func) { |
| 98 | if (ttisfunction(uv->v)) { /* object to-be-closed is a function? */ | 111 | if (ttisfunction(s2v(func))) { /* object to-be-closed is a function? */ |
| 99 | setobj2s(L, func, uv->v); /* will call it */ | 112 | setobjs2s(L, func + 1, func - 1); /* push error msg. as argument */ |
| 100 | setobjs2s(L, func + 1, level); /* error msg. as argument */ | ||
| 101 | } | 113 | } |
| 102 | else { /* try '__close' metamethod */ | 114 | else { /* try '__close' metamethod */ |
| 103 | const TValue *tm = luaT_gettmbyobj(L, uv->v, TM_CLOSE); | 115 | const TValue *tm = luaT_gettmbyobj(L, s2v(func), TM_CLOSE); |
| 104 | if (ttisnil(tm)) | 116 | if (ttisnil(tm)) /* no metamethod? */ |
| 105 | return status; /* no metamethod */ | 117 | return 0; /* nothing to call */ |
| 118 | setobjs2s(L, func + 1, func); /* 'self' is the argument */ | ||
| 106 | setobj2s(L, func, tm); /* will call metamethod */ | 119 | setobj2s(L, func, tm); /* will call metamethod */ |
| 107 | setobj2s(L, func + 1, uv->v); /* with 'self' as argument */ | ||
| 108 | } | 120 | } |
| 109 | L->top = func + 2; /* add function and argument */ | 121 | L->top = func + 2; /* add function and argument */ |
| 110 | if (status == LUA_OK) /* not in "error mode"? */ | 122 | return 1; |
| 123 | } | ||
| 124 | |||
| 125 | |||
| 126 | /* | ||
| 127 | ** Prepare and call a closing method. If status is OK, code is | ||
| 128 | ** still inside the original protected call, and so any error | ||
| 129 | ** will be handled there. Otherwise, a previous error already | ||
| 130 | ** activated original protected call, and so the call to the | ||
| 131 | ** closing method must be protected here. | ||
| 132 | */ | ||
| 133 | static int closeupval (lua_State *L, TValue *uv, StkId level, int status) { | ||
| 134 | StkId func = level + 1; /* save slot for old error message */ | ||
| 135 | if (unlikely(status != LUA_OK)) /* was there an error? */ | ||
| 136 | luaD_seterrorobj(L, status, level); /* save error message */ | ||
| 137 | else | ||
| 138 | setnilvalue(s2v(level)); /* no error message */ | ||
| 139 | setobj2s(L, func, uv); /* put object on top of error message */ | ||
| 140 | if (!prepclosingmethod(L, func)) | ||
| 141 | return status; /* nothing to call */ | ||
| 142 | if (likely(status == LUA_OK)) /* not in "error mode"? */ | ||
| 111 | callclose(L, func); /* call closing method */ | 143 | callclose(L, func); /* call closing method */ |
| 112 | else { /* already inside error handler; cannot raise another error */ | 144 | else { /* already inside error handler; cannot raise another error */ |
| 113 | int newstatus = luaD_pcall(L, callclose, func, savestack(L, level), 0); | 145 | int newstatus = luaD_pcall(L, callclose, func, savestack(L, level), 0); |
| @@ -118,6 +150,36 @@ static int closeupval (lua_State *L, UpVal *uv, StkId level, int status) { | |||
| 118 | } | 150 | } |
| 119 | 151 | ||
| 120 | 152 | ||
| 153 | /* | ||
| 154 | ** Try to create a to-be-closed upvalue | ||
| 155 | ** (can raise a memory-allocation error) | ||
| 156 | */ | ||
| 157 | static void trynewtbcupval (lua_State *L, void *ud) { | ||
| 158 | StkId level = cast(StkId, ud); | ||
| 159 | lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); | ||
| 160 | newupval(L, LUA_TUPVALTBC, level, &L->openupval); | ||
| 161 | } | ||
| 162 | |||
| 163 | |||
| 164 | /* | ||
| 165 | ** Create a to-be-closed upvalue. If there is a memory error | ||
| 166 | ** when creating the upvalue, the closing method must be called here, | ||
| 167 | ** as there is no upvalue to call it later. | ||
| 168 | */ | ||
| 169 | void luaF_newtbcupval (lua_State *L, StkId level) { | ||
| 170 | int status = luaD_rawrunprotected(L, trynewtbcupval, level); | ||
| 171 | if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ | ||
| 172 | StkId func = level + 1; | ||
| 173 | lua_assert(status == LUA_ERRMEM); | ||
| 174 | setobjs2s(L, func, level); /* open space for error message */ | ||
| 175 | luaD_seterrorobj(L, status, level); /* save error message */ | ||
| 176 | if (prepclosingmethod(L, func)) | ||
| 177 | callclose(L, func); /* call closing method */ | ||
| 178 | luaD_throw(L, LUA_ERRMEM); /* throw memory error */ | ||
| 179 | } | ||
| 180 | } | ||
| 181 | |||
| 182 | |||
| 121 | void luaF_unlinkupval (UpVal *uv) { | 183 | void luaF_unlinkupval (UpVal *uv) { |
| 122 | lua_assert(upisopen(uv)); | 184 | lua_assert(upisopen(uv)); |
| 123 | *uv->u.open.previous = uv->u.open.next; | 185 | *uv->u.open.previous = uv->u.open.next; |
| @@ -139,7 +201,7 @@ int luaF_close (lua_State *L, StkId level, int status) { | |||
| 139 | luaC_barrier(L, uv, slot); | 201 | luaC_barrier(L, uv, slot); |
| 140 | if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */ | 202 | if (status >= 0 && uv->tt == LUA_TUPVALTBC) { /* must be closed? */ |
| 141 | ptrdiff_t levelrel = savestack(L, level); | 203 | ptrdiff_t levelrel = savestack(L, level); |
| 142 | status = closeupval(L, uv, upl, status); /* may reallocate the stack */ | 204 | status = closeupval(L, uv->v, upl, status); /* may reallocate the stack */ |
| 143 | level = restorestack(L, levelrel); | 205 | level = restorestack(L, levelrel); |
| 144 | } | 206 | } |
| 145 | } | 207 | } |
