aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-13 13:54:10 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-13 13:54:10 -0300
commitb07fc10e91a5954254b47280aba287220c734a4b (patch)
treee50d4e5ef9aab68487caf0944e72a7de04bb8bb5
parentcc1692515e2a6aabc6d07159e7926656e38eda53 (diff)
downloadlua-b07fc10e91a5954254b47280aba287220c734a4b.tar.gz
lua-b07fc10e91a5954254b47280aba287220c734a4b.tar.bz2
lua-b07fc10e91a5954254b47280aba287220c734a4b.zip
Allow yields inside '__close' metamethods
Initial implementation to allow yields inside '__close' metamethods. This current version still does not allow a '__close' metamethod to yield when called due to an error. '__close' metamethods from C functions also are not allowed to yield.
-rw-r--r--lapi.c4
-rw-r--r--ldo.c6
-rw-r--r--lfunc.c20
-rw-r--r--lfunc.h2
-rw-r--r--lstate.c2
-rw-r--r--lvm.c10
-rw-r--r--testes/locals.lua88
7 files changed, 114 insertions, 18 deletions
diff --git a/lapi.c b/lapi.c
index 0f0e31af..3583e9c0 100644
--- a/lapi.c
+++ b/lapi.c
@@ -189,7 +189,7 @@ LUA_API void lua_settop (lua_State *L, int idx) {
189 } 189 }
190#if defined(LUA_COMPAT_5_4_0) 190#if defined(LUA_COMPAT_5_4_0)
191 if (diff < 0 && hastocloseCfunc(ci->nresults)) 191 if (diff < 0 && hastocloseCfunc(ci->nresults))
192 luaF_close(L, L->top + diff, CLOSEKTOP); 192 luaF_close(L, L->top + diff, CLOSEKTOP, 0);
193#endif 193#endif
194 L->top += diff; 194 L->top += diff;
195 api_check(L, L->openupval == NULL || uplevel(L->openupval) < L->top, 195 api_check(L, L->openupval == NULL || uplevel(L->openupval) < L->top,
@@ -205,7 +205,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) {
205 api_check(L, hastocloseCfunc(L->ci->nresults) && L->openupval != NULL && 205 api_check(L, hastocloseCfunc(L->ci->nresults) && L->openupval != NULL &&
206 uplevel(L->openupval) == level, 206 uplevel(L->openupval) == level,
207 "no variable to close at given level"); 207 "no variable to close at given level");
208 luaF_close(L, level, CLOSEKTOP); 208 luaF_close(L, level, CLOSEKTOP, 0);
209 setnilvalue(s2v(level)); 209 setnilvalue(s2v(level));
210 lua_unlock(L); 210 lua_unlock(L);
211} 211}
diff --git a/ldo.c b/ldo.c
index ba0c93b8..aa159cf0 100644
--- a/ldo.c
+++ b/ldo.c
@@ -406,7 +406,7 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
406 default: /* multiple results (or to-be-closed variables) */ 406 default: /* multiple results (or to-be-closed variables) */
407 if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ 407 if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */
408 ptrdiff_t savedres = savestack(L, res); 408 ptrdiff_t savedres = savestack(L, res);
409 luaF_close(L, res, CLOSEKTOP); /* may change the stack */ 409 luaF_close(L, res, CLOSEKTOP, 0); /* may change the stack */
410 res = restorestack(L, savedres); 410 res = restorestack(L, savedres);
411 wanted = codeNresults(wanted); /* correct value */ 411 wanted = codeNresults(wanted); /* correct value */
412 if (wanted == LUA_MULTRET) 412 if (wanted == LUA_MULTRET)
@@ -647,7 +647,7 @@ static void recover (lua_State *L, void *ud) {
647 /* "finish" luaD_pcall */ 647 /* "finish" luaD_pcall */
648 L->ci = ci; 648 L->ci = ci;
649 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */ 649 L->allowhook = getoah(ci->callstatus); /* restore original 'allowhook' */
650 luaF_close(L, func, status); /* may change the stack */ 650 luaF_close(L, func, status, 0); /* may change the stack */
651 func = restorestack(L, ci->u2.funcidx); 651 func = restorestack(L, ci->u2.funcidx);
652 luaD_seterrorobj(L, status, func); 652 luaD_seterrorobj(L, status, func);
653 luaD_shrinkstack(L); /* restore stack size in case of overflow */ 653 luaD_shrinkstack(L); /* restore stack size in case of overflow */
@@ -803,7 +803,7 @@ struct CloseP {
803*/ 803*/
804static void closepaux (lua_State *L, void *ud) { 804static void closepaux (lua_State *L, void *ud) {
805 struct CloseP *pcl = cast(struct CloseP *, ud); 805 struct CloseP *pcl = cast(struct CloseP *, ud);
806 luaF_close(L, pcl->level, pcl->status); 806 luaF_close(L, pcl->level, pcl->status, 0);
807} 807}
808 808
809 809
diff --git a/lfunc.c b/lfunc.c
index a8030afa..13e44d46 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -101,17 +101,21 @@ UpVal *luaF_findupval (lua_State *L, StkId level) {
101 101
102 102
103/* 103/*
104** Call closing method for object 'obj' with error message 'err'. 104** Call closing method for object 'obj' with error message 'err'. The
105** boolean 'yy' controls whether the call is yieldable.
105** (This function assumes EXTRA_STACK.) 106** (This function assumes EXTRA_STACK.)
106*/ 107*/
107static void callclosemethod (lua_State *L, TValue *obj, TValue *err) { 108static void callclosemethod (lua_State *L, TValue *obj, TValue *err, int yy) {
108 StkId top = L->top; 109 StkId top = L->top;
109 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE); 110 const TValue *tm = luaT_gettmbyobj(L, obj, TM_CLOSE);
110 setobj2s(L, top, tm); /* will call metamethod... */ 111 setobj2s(L, top, tm); /* will call metamethod... */
111 setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */ 112 setobj2s(L, top + 1, obj); /* with 'self' as the 1st argument */
112 setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */ 113 setobj2s(L, top + 2, err); /* and error msg. as 2nd argument */
113 L->top = top + 3; /* add function and arguments */ 114 L->top = top + 3; /* add function and arguments */
114 luaD_callnoyield(L, top, 0); /* call method */ 115 if (yy)
116 luaD_call(L, top, 0);
117 else
118 luaD_callnoyield(L, top, 0);
115} 119}
116 120
117 121
@@ -137,7 +141,7 @@ static void checkclosemth (lua_State *L, StkId level, const TValue *obj) {
137** the 'level' of the upvalue being closed, as everything after that 141** the 'level' of the upvalue being closed, as everything after that
138** won't be used again. 142** won't be used again.
139*/ 143*/
140static void prepcallclosemth (lua_State *L, StkId level, int status) { 144static void prepcallclosemth (lua_State *L, StkId level, int status, int yy) {
141 TValue *uv = s2v(level); /* value being closed */ 145 TValue *uv = s2v(level); /* value being closed */
142 TValue *errobj; 146 TValue *errobj;
143 if (status == CLOSEKTOP) 147 if (status == CLOSEKTOP)
@@ -146,7 +150,7 @@ static void prepcallclosemth (lua_State *L, StkId level, int status) {
146 errobj = s2v(level + 1); /* error object goes after 'uv' */ 150 errobj = s2v(level + 1); /* error object goes after 'uv' */
147 luaD_seterrorobj(L, status, level + 1); /* set error object */ 151 luaD_seterrorobj(L, status, level + 1); /* set error object */
148 } 152 }
149 callclosemethod(L, uv, errobj); 153 callclosemethod(L, uv, errobj, yy);
150} 154}
151 155
152 156
@@ -174,7 +178,7 @@ void luaF_newtbcupval (lua_State *L, StkId level) {
174 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */ 178 if (unlikely(status != LUA_OK)) { /* memory error creating upvalue? */
175 lua_assert(status == LUA_ERRMEM); 179 lua_assert(status == LUA_ERRMEM);
176 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */ 180 luaD_seterrorobj(L, LUA_ERRMEM, level + 1); /* save error message */
177 callclosemethod(L, s2v(level), s2v(level + 1)); 181 callclosemethod(L, s2v(level), s2v(level + 1), 0);
178 luaD_throw(L, LUA_ERRMEM); /* throw memory error */ 182 luaD_throw(L, LUA_ERRMEM); /* throw memory error */
179 } 183 }
180 } 184 }
@@ -194,7 +198,7 @@ void luaF_unlinkupval (UpVal *uv) {
194** to NOCLOSINGMETH closes upvalues without running any __close 198** to NOCLOSINGMETH closes upvalues without running any __close
195** metamethods. 199** metamethods.
196*/ 200*/
197void luaF_close (lua_State *L, StkId level, int status) { 201void luaF_close (lua_State *L, StkId level, int status, int yy) {
198 UpVal *uv; 202 UpVal *uv;
199 StkId upl; /* stack index pointed by 'uv' */ 203 StkId upl; /* stack index pointed by 'uv' */
200 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) { 204 while ((uv = L->openupval) != NULL && (upl = uplevel(uv)) >= level) {
@@ -209,7 +213,7 @@ void luaF_close (lua_State *L, StkId level, int status) {
209 } 213 }
210 if (uv->tbc && status != NOCLOSINGMETH) { 214 if (uv->tbc && status != NOCLOSINGMETH) {
211 ptrdiff_t levelrel = savestack(L, level); 215 ptrdiff_t levelrel = savestack(L, level);
212 prepcallclosemth(L, upl, status); /* may change the stack */ 216 prepcallclosemth(L, upl, status, yy); /* may change the stack */
213 level = restorestack(L, levelrel); 217 level = restorestack(L, levelrel);
214 } 218 }
215 } 219 }
diff --git a/lfunc.h b/lfunc.h
index 40de4636..2e6df535 100644
--- a/lfunc.h
+++ b/lfunc.h
@@ -59,7 +59,7 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals);
59LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); 59LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl);
60LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); 60LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level);
61LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); 61LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level);
62LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status); 62LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy);
63LUAI_FUNC void luaF_unlinkupval (UpVal *uv); 63LUAI_FUNC void luaF_unlinkupval (UpVal *uv);
64LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); 64LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f);
65LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, 65LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number,
diff --git a/lstate.c b/lstate.c
index a6ef82a3..92ccbf9b 100644
--- a/lstate.c
+++ b/lstate.c
@@ -313,7 +313,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
313 313
314void luaE_freethread (lua_State *L, lua_State *L1) { 314void luaE_freethread (lua_State *L, lua_State *L1) {
315 LX *l = fromstate(L1); 315 LX *l = fromstate(L1);
316 luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues */ 316 luaF_close(L1, L1->stack, NOCLOSINGMETH, 0); /* close all upvalues */
317 lua_assert(L1->openupval == NULL); 317 lua_assert(L1->openupval == NULL);
318 luai_userstatefree(L, L1); 318 luai_userstatefree(L, L1);
319 freestack(L1); 319 freestack(L1);
diff --git a/lvm.c b/lvm.c
index a6f04606..d6c05bbd 100644
--- a/lvm.c
+++ b/lvm.c
@@ -842,6 +842,10 @@ void luaV_finishOp (lua_State *L) {
842 luaV_concat(L, total); /* concat them (may yield again) */ 842 luaV_concat(L, total); /* concat them (may yield again) */
843 break; 843 break;
844 } 844 }
845 case OP_CLOSE: case OP_RETURN: { /* yielded closing variables */
846 ci->u.l.savedpc--; /* repeat instruction to close other vars. */
847 break;
848 }
845 default: { 849 default: {
846 /* only these other opcodes can yield */ 850 /* only these other opcodes can yield */
847 lua_assert(op == OP_TFORCALL || op == OP_CALL || 851 lua_assert(op == OP_TFORCALL || op == OP_CALL ||
@@ -1524,7 +1528,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1524 vmbreak; 1528 vmbreak;
1525 } 1529 }
1526 vmcase(OP_CLOSE) { 1530 vmcase(OP_CLOSE) {
1527 Protect(luaF_close(L, ra, LUA_OK)); 1531 Protect(luaF_close(L, ra, LUA_OK, 1));
1528 vmbreak; 1532 vmbreak;
1529 } 1533 }
1530 vmcase(OP_TBC) { 1534 vmcase(OP_TBC) {
@@ -1632,7 +1636,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1632 /* close upvalues from current call; the compiler ensures 1636 /* close upvalues from current call; the compiler ensures
1633 that there are no to-be-closed variables here, so this 1637 that there are no to-be-closed variables here, so this
1634 call cannot change the stack */ 1638 call cannot change the stack */
1635 luaF_close(L, base, NOCLOSINGMETH); 1639 luaF_close(L, base, NOCLOSINGMETH, 0);
1636 lua_assert(base == ci->func + 1); 1640 lua_assert(base == ci->func + 1);
1637 } 1641 }
1638 while (!ttisfunction(s2v(ra))) { /* not a function? */ 1642 while (!ttisfunction(s2v(ra))) { /* not a function? */
@@ -1662,7 +1666,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1662 if (TESTARG_k(i)) { /* may there be open upvalues? */ 1666 if (TESTARG_k(i)) { /* may there be open upvalues? */
1663 if (L->top < ci->top) 1667 if (L->top < ci->top)
1664 L->top = ci->top; 1668 L->top = ci->top;
1665 luaF_close(L, base, CLOSEKTOP); 1669 luaF_close(L, base, CLOSEKTOP, 1);
1666 updatetrap(ci); 1670 updatetrap(ci);
1667 updatestack(ci); 1671 updatestack(ci);
1668 } 1672 }
diff --git a/testes/locals.lua b/testes/locals.lua
index add023ca..c9c93ccf 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -641,6 +641,94 @@ end
641print "to-be-closed variables in coroutines" 641print "to-be-closed variables in coroutines"
642 642
643do 643do
644 -- yielding inside closing metamethods
645
646 local function checktable (t1, t2)
647 assert(#t1 == #t2)
648 for i = 1, #t1 do
649 assert(t1[i] == t2[i])
650 end
651 end
652
653 local trace = {}
654 local co = coroutine.wrap(function ()
655
656 trace[#trace + 1] = "nowX"
657
658 -- will be closed after 'y'
659 local x <close> = func2close(function (_, msg)
660 assert(msg == nil)
661 trace[#trace + 1] = "x1"
662 coroutine.yield("x")
663 trace[#trace + 1] = "x2"
664 end)
665
666 return pcall(function ()
667 do -- 'z' will be closed first
668 local z <close> = func2close(function (_, msg)
669 assert(msg == nil)
670 trace[#trace + 1] = "z1"
671 coroutine.yield("z")
672 trace[#trace + 1] = "z2"
673 end)
674 end
675
676 trace[#trace + 1] = "nowY"
677
678 -- will be closed after 'z'
679 local y <close> = func2close(function(_, msg)
680 assert(msg == nil)
681 trace[#trace + 1] = "y1"
682 coroutine.yield("y")
683 trace[#trace + 1] = "y2"
684 end)
685
686 return 10, 20, 30
687 end)
688 end)
689
690 assert(co() == "z")
691 assert(co() == "y")
692 assert(co() == "x")
693 checktable({co()}, {true, 10, 20, 30})
694 checktable(trace, {"nowX", "z1", "z2", "nowY", "y1", "y2", "x1", "x2"})
695
696end
697
698
699do
700 -- yielding inside closing metamethods after an error:
701 -- not yet implemented; raises an error
702
703 local co = coroutine.wrap(function ()
704
705 local function foo (err)
706
707 local x <close> = func2close(function(_, msg)
708 assert(msg == err)
709 coroutine.yield("x")
710 return 100, 200
711 end)
712
713 if err then error(err) else return 10, 20 end
714 end
715
716 coroutine.yield(pcall(foo, nil)) -- no error
717 return pcall(foo, 10) -- 'foo' will raise an error
718 end)
719
720 local a, b = co()
721 assert(a == "x" and b == nil) -- yields inside 'x'; Ok
722
723 local a, b, c = co()
724 assert(a and b == 10 and c == 20) -- returns from 'pcall(foo, nil)'
725
726 local st, msg = co() -- error yielding after an error
727 assert(not st and string.find(msg, "attempt to yield"))
728end
729
730
731do
644 -- an error in a wrapped coroutine closes variables 732 -- an error in a wrapped coroutine closes variables
645 local x = false 733 local x = false
646 local y = false 734 local y = false