aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-29 10:23:02 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-12-29 10:23:02 -0300
commit59e565d9555c07e82808d8c1db8f4f4d159b5e5c (patch)
tree7dced733d2e01a6060d656afad903539ac2fedc9
parent6188f3a654c0380db08eb40a5465ce8e71c784f5 (diff)
downloadlua-59e565d9555c07e82808d8c1db8f4f4d159b5e5c.tar.gz
lua-59e565d9555c07e82808d8c1db8f4f4d159b5e5c.tar.bz2
lua-59e565d9555c07e82808d8c1db8f4f4d159b5e5c.zip
No need to recheck close method before calling it
A to-be-closed variable is constant and it must have a close metamethod when it is created. A program has to go out of its way (e.g., by changing the variable's metamethod) to invalidate that check. So, it is not worth to test that again. If the program tampers with the metamethod, Lua will raise a regular error when attempting to call it.
-rw-r--r--lfunc.c44
-rw-r--r--testes/locals.lua46
2 files changed, 63 insertions, 27 deletions
diff --git a/lfunc.c b/lfunc.c
index ae68487c..a8030afa 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -101,31 +101,32 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
101 101
102 102
103/* 103/*
104** Prepare closing method plus its arguments for object 'obj' with 104** Call closing method for object 'obj' with error message 'err'.
105** error message 'err'. (This function assumes EXTRA_STACK.) 105** (This function assumes EXTRA_STACK.)
106*/ 106*/
107static int prepclosingmethod (lua_State *L, TValue *obj, TValue *err) { 107static void callclosemethod (lua_State *L, TValue *obj, TValue *err) {
108 StkId top = L->top; 108 StkId top = L->top;
109 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); 109 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
110 if (ttisnil(tm)) /* no metamethod? */
111 return 0; /* nothing to call */
112 setobj2s(L, top, tm); /* will call metamethod... */ 110 setobj2s(L, top, tm); /* will call metamethod... */
113 setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ 111 setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
114 setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ 112 setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
115 L->top = top + 3; /* add function and arguments */ 113 L->top = top + 3; /* add function and arguments */
116 return 1; 114 luaD_callnoyield(L, top, 0); /* call method */
117} 115}
118 116
119 117
120/* 118/*
121** Raise an error with message 'msg', inserting the name of the 119** Check whether 'obj' has a close metamethod and raise an error
122** local variable at position 'level' in the stack. 120** if not.
123*/ 121*/
124static void varerror (lua_State *L, StkId level, const char *msg) { 122static void checkclosemth (lua_State *L, StkId level, const TValue *obj) {
125 int idx = cast_int(level - L->ci->func); 123 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
126 const char *vname = luaG_findlocal(L, L->ci, idx, NULL); 124 if (ttisnil(tm)) { /* no metamethod? */
127 if (vname == NULL) vname = "?"; 125 int idx = cast_int(level - L->ci->func); /* variable index */
128 luaG_runerror(L, msg, vname); 126 const char *vname = luaG_findlocal(L, L->ci, idx, NULL);
127 if (vname == NULL) vname = "?";
128 luaG_runerror(L, "variable '%s' got a non-closable value", vname);
129 }
129} 130}
130 131
131 132
@@ -136,7 +137,7 @@ static void varerror (lua_State *L, StkId level, const char *msg) {
136** the 'level' of the upvalue being closed, as everything after that 137** the 'level' of the upvalue being closed, as everything after that
137** won't be used again. 138** won't be used again.
138*/ 139*/
139static void callclosemth (lua_State *L, StkId level, int status) { 140static void prepcallclosemth (lua_State *L, StkId level, int status) {
140 TValue *uv = s2v(level); /* value being closed */ 141 TValue *uv = s2v(level); /* value being closed */
141 TValue *errobj; 142 TValue *errobj;
142 if (status == CLOSEKTOP) 143 if (status == CLOSEKTOP)
@@ -145,10 +146,7 @@ static void callclosemth (lua_State *L, StkId level, int status) {
145 errobj = s2v(level + 1); /* error object goes after 'uv' */ 146 errobj = s2v(level + 1); /* error object goes after 'uv' */
146 luaD_seterrorobj(L, status, level + 1); /* set error object */ 147 luaD_seterrorobj(L, status, level + 1); /* set error object */
147 } 148 }
148 if (prepclosingmethod(L, uv, errobj)) /* something to call? */ 149 callclosemethod(L, uv, errobj);
149 luaD_callnoyield(L, L->top - 3, 0); /* call method */
150 else if (!l_isfalse(uv)) /* non-closable non-false value? */
151 varerror(L, level, "attempt to close non-closable variable '%s'");
152} 150}
153 151
154 152
@@ -171,16 +169,12 @@ void luaF_newtbcupval (lua_State *L, StkId level) {
171 lua_assert(L->openupval == NULL || uplevel(L->openupval) < level); 169 lua_assert(L->openupval == NULL || uplevel(L->openupval) < level);
172 if (!l_isfalse(obj)) { /* false doesn't need to be closed */ 170 if (!l_isfalse(obj)) { /* false doesn't need to be closed */
173 int status; 171 int status;
174 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); 172 checkclosemth(L, level, obj);
175 if (ttisnil(tm)) /* no metamethod? */
176 varerror(L, level, "variable '%s' got a non-closable value");
177 status = luaD_rawrunprotected(L, trynewtbcupval, level); 173 status = luaD_rawrunprotected(L, trynewtbcupval, level);
178 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ 174 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
179 lua_assert(status == LUA_ERRMEM); 175 lua_assert(status == LUA_ERRMEM);
180 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ 176 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
181 /* next call must succeed, as object is closable */ 177 callclosemethod(L, s2v(level), s2v(level + 1));
182 prepclosingmethod(L, s2v(level), s2v(level + 1));
183 luaD_callnoyield(L, L->top - 3, 0); /* call method */
184 luaD_throw(L, LUA_ERRMEM); /* throw memory error */ 178 luaD_throw(L, LUA_ERRMEM); /* throw memory error */
185 } 179 }
186 } 180 }
@@ -215,7 +209,7 @@ void luaF_close (lua_State *L, StkId level, int status) {
215 } 209 }
216 if (uv->tbc && status != NOCLOSINGMETH) { 210 if (uv->tbc && status != NOCLOSINGMETH) {
217 ptrdiff_t levelrel = savestack(L, level); 211 ptrdiff_t levelrel = savestack(L, level);
218 callclosemth(L, upl, status); /* may change the stack */ 212 prepcallclosemth(L, upl, status); /* may change the stack */
219 level = restorestack(L, levelrel); 213 level = restorestack(L, levelrel);
220 } 214 }
221 } 215 }
diff --git a/testes/locals.lua b/testes/locals.lua
index 84e0b03a..1b43609b 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -459,8 +459,50 @@ do -- errors due to non-closable values
459 getmetatable(xyz).__close = nil -- remove metamethod 459 getmetatable(xyz).__close = nil -- remove metamethod
460 end 460 end
461 local stat, msg = pcall(foo) 461 local stat, msg = pcall(foo)
462 assert(not stat and 462 assert(not stat and string.find(msg, "attempt to call a nil value"))
463 string.find(msg, "attempt to close non%-closable variable 'xyz'")) 463end
464
465
466do -- tbc inside close methods
467 local track = {}
468 local function foo ()
469 local x <close> = func2close(function ()
470 local xx <close> = func2close(function (_, msg)
471 assert(msg == nil)
472 track[#track + 1] = "xx"
473 end)
474 track[#track + 1] = "x"
475 end)
476 track[#track + 1] = "foo"
477 return 20, 30, 40
478 end
479 local a, b, c, d = foo()
480 assert(a == 20 and b == 30 and c == 40 and d == nil)
481 assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx")
482
483 -- again, with errors
484 local track = {}
485 local function foo ()
486 local x0 <close> = func2close(function (_, msg)
487 assert(msg == 202)
488 track[#track + 1] = "x0"
489 end)
490 local x <close> = func2close(function ()
491 local xx <close> = func2close(function (_, msg)
492 assert(msg == 101)
493 track[#track + 1] = "xx"
494 error(202)
495 end)
496 track[#track + 1] = "x"
497 error(101)
498 end)
499 track[#track + 1] = "foo"
500 return 20, 30, 40
501 end
502 local st, msg = pcall(foo)
503 assert(not st and msg == 202)
504 assert(track[1] == "foo" and track[2] == "x" and track[3] == "xx" and
505 track[4] == "x0")
464end 506end
465 507
466 508