diff options
Diffstat (limited to '')
| -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 |
