aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-04-16 15:41:44 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-04-16 15:41:44 -0300
commit681297187ec45268e872b26753c441586c12bdd8 (patch)
tree90e62c82146470a4c4931801a36f66de0bc92318
parent5148954eed7550dcac587fce0214b470442efa0d (diff)
downloadlua-681297187ec45268e872b26753c441586c12bdd8.tar.gz
lua-681297187ec45268e872b26753c441586c12bdd8.tar.bz2
lua-681297187ec45268e872b26753c441586c12bdd8.zip
Bug: yielding in '__close' mess up number of returns
Yielding in a __close metamethod called when returning vararg results changes the top and so messes up the number of returned values.
-rw-r--r--lstate.h2
-rw-r--r--lvm.c12
-rw-r--r--testes/locals.lua59
3 files changed, 71 insertions, 2 deletions
diff --git a/lstate.h b/lstate.h
index c1283bb6..44cf939c 100644
--- a/lstate.h
+++ b/lstate.h
@@ -165,7 +165,7 @@ typedef struct stringtable {
165** - field 'nyield' is used only while a function is "doing" an 165** - field 'nyield' is used only while a function is "doing" an
166** yield (from the yield until the next resume); 166** yield (from the yield until the next resume);
167** - field 'nres' is used only while closing tbc variables when 167** - field 'nres' is used only while closing tbc variables when
168** returning from a C function; 168** returning from a function;
169** - field 'transferinfo' is used only during call/returnhooks, 169** - field 'transferinfo' is used only during call/returnhooks,
170** before the function starts or after it ends. 170** before the function starts or after it ends.
171*/ 171*/
diff --git a/lvm.c b/lvm.c
index 16e01d68..e4b1903e 100644
--- a/lvm.c
+++ b/lvm.c
@@ -847,10 +847,19 @@ void luaV_finishOp (lua_State *L) {
847 luaV_concat(L, total); /* concat them (may yield again) */ 847 luaV_concat(L, total); /* concat them (may yield again) */
848 break; 848 break;
849 } 849 }
850 case OP_CLOSE: case OP_RETURN: { /* yielded closing variables */ 850 case OP_CLOSE: { /* yielded closing variables */
851 ci->u.l.savedpc--; /* repeat instruction to close other vars. */ 851 ci->u.l.savedpc--; /* repeat instruction to close other vars. */
852 break; 852 break;
853 } 853 }
854 case OP_RETURN: { /* yielded closing variables */
855 StkId ra = base + GETARG_A(inst);
856 /* adjust top to signal correct number of returns, in case the
857 return is "up to top" ('isIT') */
858 L->top = ra + ci->u2.nres;
859 /* repeat instruction to close other vars. and complete the return */
860 ci->u.l.savedpc--;
861 break;
862 }
854 default: { 863 default: {
855 /* only these other opcodes can yield */ 864 /* only these other opcodes can yield */
856 lua_assert(op == OP_TFORCALL || op == OP_CALL || 865 lua_assert(op == OP_TFORCALL || op == OP_CALL ||
@@ -1672,6 +1681,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1672 n = cast_int(L->top - ra); /* get what is available */ 1681 n = cast_int(L->top - ra); /* get what is available */
1673 savepc(ci); 1682 savepc(ci);
1674 if (TESTARG_k(i)) { /* may there be open upvalues? */ 1683 if (TESTARG_k(i)) { /* may there be open upvalues? */
1684 ci->u2.nres = n; /* save number of returns */
1675 if (L->top < ci->top) 1685 if (L->top < ci->top)
1676 L->top = ci->top; 1686 L->top = ci->top;
1677 luaF_close(L, base, CLOSEKTOP, 1); 1687 luaF_close(L, base, CLOSEKTOP, 1);
diff --git a/testes/locals.lua b/testes/locals.lua
index 6aad5d25..6151f64d 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -814,6 +814,65 @@ end
814 814
815 815
816do 816do
817 -- yielding inside closing metamethods while returning
818 -- (bug in 5.4.3)
819
820 local extrares -- result from extra yield (if any)
821
822 local function check (body, extra, ...)
823 local t = table.pack(...) -- expected returns
824 local co = coroutine.wrap(body)
825 if extra then
826 extrares = co() -- runs until first (extra) yield
827 end
828 local res = table.pack(co()) -- runs until yield inside '__close'
829 assert(res.n == 2 and res[2] == nil)
830 local res2 = table.pack(co()) -- runs until end of function
831 assert(res2.n == t.n)
832 for i = 1, #t do
833 if t[i] == "x" then
834 assert(res2[i] == res[1]) -- value that was closed
835 else
836 assert(res2[i] == t[i])
837 end
838 end
839 end
840
841 local function foo ()
842 local x <close> = func2close(coroutine.yield)
843 local extra <close> = func2close(function (self)
844 assert(self == extrares)
845 coroutine.yield(100)
846 end)
847 extrares = extra
848 return table.unpack{10, x, 30}
849 end
850 check(foo, true, 10, "x", 30)
851 assert(extrares == 100)
852
853 local function foo ()
854 local x <close> = func2close(coroutine.yield)
855 return
856 end
857 check(foo, false)
858
859 local function foo ()
860 local x <close> = func2close(coroutine.yield)
861 local y, z = 20, 30
862 return x
863 end
864 check(foo, false, "x")
865
866 local function foo ()
867 local x <close> = func2close(coroutine.yield)
868 local extra <close> = func2close(coroutine.yield)
869 return table.unpack({}, 1, 100) -- 100 nils
870 end
871 check(foo, true, table.unpack({}, 1, 100))
872
873end
874
875do
817 -- yielding inside closing metamethods after an error 876 -- yielding inside closing metamethods after an error
818 877
819 local co = coroutine.wrap(function () 878 local co = coroutine.wrap(function ()