diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-07-31 10:43:51 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-07-31 10:43:51 -0300 |
| commit | f645d3157372c73573dff221c5b26691cb0e7d56 (patch) | |
| tree | 61adb1f332bbd8c0c0365b81cef8de47fa2ea06a /lfunc.c | |
| parent | 35b4efc270db2418bc2cac6671575a45028061c3 (diff) | |
| download | lua-f645d3157372c73573dff221c5b26691cb0e7d56.tar.gz lua-f645d3157372c73573dff221c5b26691cb0e7d56.tar.bz2 lua-f645d3157372c73573dff221c5b26691cb0e7d56.zip | |
To-be-closed variables must be closed on initialization
When initializing a to-be-closed variable, check whether it has a
'__close' metamethod (or is a false value) and raise an error if
if it hasn't. This produces more accurate error messages. (The
check before closing still need to be done: in the C API, the value
is not constant; and the object may lose its '__close' metamethod
during the block.)
Diffstat (limited to 'lfunc.c')
| -rw-r--r-- | lfunc.c | 49 |
1 files changed, 32 insertions, 17 deletions
| @@ -124,11 +124,23 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { | |||
| 124 | 124 | ||
| 125 | 125 | ||
| 126 | /* | 126 | /* |
| 127 | ** Raise an error with message 'msg', inserting the name of the | ||
| 128 | ** local variable at position 'level' in the stack. | ||
| 129 | */ | ||
| 130 | static void varerror (lua_State *L, StkId level, const char *msg) { | ||
| 131 | int idx = cast_int(level - L->ci->func); | ||
| 132 | const char *vname = luaG_findlocal(L, L->ci, idx, NULL); | ||
| 133 | if (vname == NULL) vname = "?"; | ||
| 134 | luaG_runerror(L, msg, vname); | ||
| 135 | } | ||
| 136 | |||
| 137 | |||
| 138 | /* | ||
| 127 | ** Prepare and call a closing method. If status is OK, code is still | 139 | ** Prepare and call a closing method. If status is OK, code is still |
| 128 | ** inside the original protected call, and so any error will be handled | 140 | ** inside the original protected call, and so any error will be handled |
| 129 | ** there. Otherwise, a previous error already activated original | 141 | ** there. Otherwise, a previous error already activated the original |
| 130 | ** protected call, and so the call to the closing method must be | 142 | ** protected call, and so the call to the closing method must be |
| 131 | ** protected here. (A status = CLOSEPROTECT behaves like a previous | 143 | ** protected here. (A status == CLOSEPROTECT behaves like a previous |
| 132 | ** error, to also run the closing method in protected mode). | 144 | ** error, to also run the closing method in protected mode). |
| 133 | ** If status is OK, the call to the closing method will be pushed | 145 | ** If status is OK, the call to the closing method will be pushed |
| 134 | ** at the top of the stack. Otherwise, values are pushed after | 146 | ** at the top of the stack. Otherwise, values are pushed after |
| @@ -140,12 +152,8 @@ static int callclosemth (lua_State *L, StkId level, int status) { | |||
| 140 | if (likely(status == LUA_OK)) { | 152 | if (likely(status == LUA_OK)) { |
| 141 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ | 153 | if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ |
| 142 | callclose(L, NULL); /* call closing method */ | 154 | callclose(L, NULL); /* call closing method */ |
| 143 | else if (!ttisnil(uv)) { /* non-closable non-nil value? */ | 155 | else if (!l_isfalse(uv)) /* non-closable non-false value? */ |
| 144 | int idx = cast_int(level - L->ci->func); | 156 | varerror(L, level, "attempt to close non-closable variable '%s'"); |
| 145 | const char *vname = luaG_findlocal(L, L->ci, idx, NULL); | ||
| 146 | if (vname == NULL) vname = "?"; | ||
| 147 | luaG_runerror(L, "attempt to close non-closable variable '%s'", vname); | ||
| 148 | } | ||
| 149 | } | 157 | } |
| 150 | else { /* must close the object in protected mode */ | 158 | else { /* must close the object in protected mode */ |
| 151 | ptrdiff_t oldtop; | 159 | ptrdiff_t oldtop; |
| @@ -170,9 +178,7 @@ static int callclosemth (lua_State *L, StkId level, int status) { | |||
| 170 | ** (can raise a memory-allocation error) | 178 | ** (can raise a memory-allocation error) |
| 171 | */ | 179 | */ |
| 172 | static void trynewtbcupval (lua_State *L, void *ud) { | 180 | static void trynewtbcupval (lua_State *L, void *ud) { |
| 173 | StkId level = cast(StkId, ud); | 181 | newupval(L, 1, cast(StkId, ud), &L->openupval); |
| 174 | lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); | ||
| 175 | newupval(L, 1, level, &L->openupval); | ||
| 176 | } | 182 | } |
| 177 | 183 | ||
| 178 | 184 | ||
| @@ -182,13 +188,22 @@ static void trynewtbcupval (lua_State *L, void *ud) { | |||
| 182 | ** as there is no upvalue to call it later. | 188 | ** as there is no upvalue to call it later. |
| 183 | */ | 189 | */ |
| 184 | void luaF_newtbcupval (lua_State *L, StkId level) { | 190 | void luaF_newtbcupval (lua_State *L, StkId level) { |
| 185 | int status = luaD_rawrunprotected(L, trynewtbcupval, level); | 191 | TValue *obj = s2v(level); |
| 186 | if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ | 192 | lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); |
| 187 | lua_assert(status == LUA_ERRMEM); | 193 | if (!l_isfalse(obj)) { /* false doesn't need to be closed */ |
| 188 | luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ | 194 | int status; |
| 189 | if (prepclosingmethod(L, s2v(level), s2v(level + 1))) | 195 | const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); |
| 196 | if (ttisnil(tm)) /* no metamethod? */ | ||
| 197 | varerror(L, level, "variable '%s' got a non-closable value"); | ||
| 198 | status = luaD_rawrunprotected(L, trynewtbcupval, level); | ||
| 199 | if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ | ||
| 200 | lua_assert(status == LUA_ERRMEM); | ||
| 201 | luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ | ||
| 202 | /* next call must succeed, as object is closable */ | ||
| 203 | prepclosingmethod(L, s2v(level), s2v(level + 1)); | ||
| 190 | callclose(L, NULL); /* call closing method */ | 204 | callclose(L, NULL); /* call closing method */ |
| 191 | luaD_throw(L, LUA_ERRMEM); /* throw memory error */ | 205 | luaD_throw(L, LUA_ERRMEM); /* throw memory error */ |
| 206 | } | ||
| 192 | } | 207 | } |
| 193 | } | 208 | } |
| 194 | 209 | ||
