diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-13 13:54:10 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-13 13:54:10 -0300 |
commit | b07fc10e91a5954254b47280aba287220c734a4b (patch) | |
tree | e50d4e5ef9aab68487caf0944e72a7de04bb8bb5 | |
parent | cc1692515e2a6aabc6d07159e7926656e38eda53 (diff) | |
download | lua-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.c | 4 | ||||
-rw-r--r-- | ldo.c | 6 | ||||
-rw-r--r-- | lfunc.c | 20 | ||||
-rw-r--r-- | lfunc.h | 2 | ||||
-rw-r--r-- | lstate.c | 2 | ||||
-rw-r--r-- | lvm.c | 10 | ||||
-rw-r--r-- | testes/locals.lua | 88 |
7 files changed, 114 insertions, 18 deletions
@@ -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 | } |
@@ -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 | */ |
804 | static void closepaux (lua_State *L, void *ud) { | 804 | static 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 | ||
@@ -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 | */ |
107 | static void callclosemethod (lua_State *L, TValue *obj, TValue *err) { | 108 | static 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 | */ |
140 | static void prepcallclosemth (lua_State *L, StkId level, int status) { | 144 | static 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 | */ |
197 | void luaF_close (lua_State *L, StkId level, int status) { | 201 | void 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 | } |
@@ -59,7 +59,7 @@ LUAI_FUNC LClosure *luaF_newLclosure (lua_State *L, int nupvals); | |||
59 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); | 59 | LUAI_FUNC void luaF_initupvals (lua_State *L, LClosure *cl); |
60 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); | 60 | LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); |
61 | LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); | 61 | LUAI_FUNC void luaF_newtbcupval (lua_State *L, StkId level); |
62 | LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status); | 62 | LUAI_FUNC void luaF_close (lua_State *L, StkId level, int status, int yy); |
63 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); | 63 | LUAI_FUNC void luaF_unlinkupval (UpVal *uv); |
64 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); | 64 | LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); |
65 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, | 65 | LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, |
@@ -313,7 +313,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) { | |||
313 | 313 | ||
314 | void luaE_freethread (lua_State *L, lua_State *L1) { | 314 | void 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); |
@@ -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 | |||
641 | print "to-be-closed variables in coroutines" | 641 | print "to-be-closed variables in coroutines" |
642 | 642 | ||
643 | do | 643 | do |
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 | |||
696 | end | ||
697 | |||
698 | |||
699 | do | ||
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")) | ||
728 | end | ||
729 | |||
730 | |||
731 | do | ||
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 |