From f645d3157372c73573dff221c5b26691cb0e7d56 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 31 Jul 2019 10:43:51 -0300 Subject: 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.) --- lfunc.c | 49 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 32 insertions(+), 17 deletions(-) (limited to 'lfunc.c') diff --git a/lfunc.c b/lfunc.c index 6f2f897f..8f39f6b0 100644 --- a/lfunc.c +++ b/lfunc.c @@ -123,12 +123,24 @@ static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { } +/* +** Raise an error with message 'msg', inserting the name of the +** local variable at position 'level' in the stack. +*/ +static void varerror (lua_State *L, StkId level, const char *msg) { + int idx = cast_int(level - L->ci->func); + const char *vname = luaG_findlocal(L, L->ci, idx, NULL); + if (vname == NULL) vname = "?"; + luaG_runerror(L, msg, vname); +} + + /* ** Prepare and call a closing method. If status is OK, code is still ** inside the original protected call, and so any error will be handled -** there. Otherwise, a previous error already activated original +** there. Otherwise, a previous error already activated the original ** protected call, and so the call to the closing method must be -** protected here. (A status = CLOSEPROTECT behaves like a previous +** protected here. (A status == CLOSEPROTECT behaves like a previous ** error, to also run the closing method in protected mode). ** If status is OK, the call to the closing method will be pushed ** 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) { if (likely(status == LUA_OK)) { if (prepclosingmethod(L, uv, &G(L)->nilvalue)) /* something to call? */ callclose(L, NULL); /* call closing method */ - else if (!ttisnil(uv)) { /* non-closable non-nil value? */ - int idx = cast_int(level - L->ci->func); - const char *vname = luaG_findlocal(L, L->ci, idx, NULL); - if (vname == NULL) vname = "?"; - luaG_runerror(L, "attempt to close non-closable variable '%s'", vname); - } + else if (!l_isfalse(uv)) /* non-closable non-false value? */ + varerror(L, level, "attempt to close non-closable variable '%s'"); } else { /* must close the object in protected mode */ ptrdiff_t oldtop; @@ -170,9 +178,7 @@ static int callclosemth (lua_State *L, StkId level, int status) { ** (can raise a memory-allocation error) */ static void trynewtbcupval (lua_State *L, void *ud) { - StkId level = cast(StkId, ud); - lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); - newupval(L, 1, level, &L->openupval); + newupval(L, 1, cast(StkId, ud), &L->openupval); } @@ -182,13 +188,22 @@ static void trynewtbcupval (lua_State *L, void *ud) { ** as there is no upvalue to call it later. */ void luaF_newtbcupval (lua_State *L, StkId level) { - int status = luaD_rawrunprotected(L, trynewtbcupval, level); - if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ - lua_assert(status == LUA_ERRMEM); - luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ - if (prepclosingmethod(L, s2v(level), s2v(level + 1))) + TValue *obj = s2v(level); + lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); + if (!l_isfalse(obj)) { /* false doesn't need to be closed */ + int status; + const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); + if (ttisnil(tm)) /* no metamethod? */ + varerror(L, level, "variable '%s' got a non-closable value"); + status = luaD_rawrunprotected(L, trynewtbcupval, level); + if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ + lua_assert(status == LUA_ERRMEM); + luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ + /* next call must succeed, as object is closable */ + prepclosingmethod(L, s2v(level), s2v(level + 1)); callclose(L, NULL); /* call closing method */ - luaD_throw(L, LUA_ERRMEM); /* throw memory error */ + luaD_throw(L, LUA_ERRMEM); /* throw memory error */ + } } } -- cgit v1.2.3-55-g6feb