diff options
| -rw-r--r-- | lapi.h | 2 | ||||
| -rw-r--r-- | ldo.c | 72 | ||||
| -rw-r--r-- | lstate.h | 12 | ||||
| -rw-r--r-- | manual/manual.of | 3 | ||||
| -rw-r--r-- | testes/locals.lua | 76 |
5 files changed, 131 insertions, 34 deletions
| @@ -42,6 +42,8 @@ | |||
| 42 | 42 | ||
| 43 | #define hastocloseCfunc(n) ((n) < LUA_MULTRET) | 43 | #define hastocloseCfunc(n) ((n) < LUA_MULTRET) |
| 44 | 44 | ||
| 45 | /* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ | ||
| 45 | #define codeNresults(n) (-(n) - 3) | 46 | #define codeNresults(n) (-(n) - 3) |
| 47 | #define decodeNresults(n) (-(n) - 3) | ||
| 46 | 48 | ||
| 47 | #endif | 49 | #endif |
| @@ -408,24 +408,27 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) { | |||
| 408 | case LUA_MULTRET: | 408 | case LUA_MULTRET: |
| 409 | wanted = nres; /* we want all results */ | 409 | wanted = nres; /* we want all results */ |
| 410 | break; | 410 | break; |
| 411 | default: /* multiple results (or to-be-closed variables) */ | 411 | default: /* two/more results and/or to-be-closed variables */ |
| 412 | if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ | 412 | if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ |
| 413 | ptrdiff_t savedres = savestack(L, res); | 413 | ptrdiff_t savedres = savestack(L, res); |
| 414 | luaF_close(L, res, CLOSEKTOP, 0); /* may change the stack */ | 414 | L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ |
| 415 | wanted = codeNresults(wanted); /* correct value */ | 415 | L->ci->u2.nres = nres; |
| 416 | if (wanted == LUA_MULTRET) | 416 | luaF_close(L, res, CLOSEKTOP, 1); |
| 417 | wanted = nres; | 417 | L->ci->callstatus &= ~CIST_CLSRET; |
| 418 | if (L->hookmask) /* if needed, call hook after '__close's */ | 418 | if (L->hookmask) /* if needed, call hook after '__close's */ |
| 419 | rethook(L, L->ci, nres); | 419 | rethook(L, L->ci, nres); |
| 420 | res = restorestack(L, savedres); /* close and hook can move stack */ | 420 | res = restorestack(L, savedres); /* close and hook can move stack */ |
| 421 | wanted = decodeNresults(wanted); | ||
| 422 | if (wanted == LUA_MULTRET) | ||
| 423 | wanted = nres; /* we want all results */ | ||
| 421 | } | 424 | } |
| 422 | break; | 425 | break; |
| 423 | } | 426 | } |
| 427 | /* generic case */ | ||
| 424 | firstresult = L->top - nres; /* index of first result */ | 428 | firstresult = L->top - nres; /* index of first result */ |
| 425 | /* move all results to correct place */ | 429 | if (nres > wanted) /* extra results? */ |
| 426 | if (nres > wanted) | 430 | nres = wanted; /* don't need them */ |
| 427 | nres = wanted; /* don't need more than that */ | 431 | for (i = 0; i < nres; i++) /* move all results to correct place */ |
| 428 | for (i = 0; i < nres; i++) | ||
| 429 | setobjs2s(L, res + i, firstresult + i); | 432 | setobjs2s(L, res + i, firstresult + i); |
| 430 | for (; i < wanted; i++) /* complete wanted number of results */ | 433 | for (; i < wanted; i++) /* complete wanted number of results */ |
| 431 | setnilvalue(s2v(res + i)); | 434 | setnilvalue(s2v(res + i)); |
| @@ -445,6 +448,9 @@ void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | |||
| 445 | rethook(L, ci, nres); | 448 | rethook(L, ci, nres); |
| 446 | /* move results to proper place */ | 449 | /* move results to proper place */ |
| 447 | moveresults(L, ci->func, nres, wanted); | 450 | moveresults(L, ci->func, nres, wanted); |
| 451 | /* function cannot be in any of these cases when returning */ | ||
| 452 | lua_assert(!(ci->callstatus & | ||
| 453 | (CIST_HOOKED | CIST_YPCALL | CIST_FIN | CIST_TRAN | CIST_CLSRET))); | ||
| 448 | L->ci = ci->previous; /* back to caller (after closing variables) */ | 454 | L->ci = ci->previous; /* back to caller (after closing variables) */ |
| 449 | } | 455 | } |
| 450 | 456 | ||
| @@ -615,28 +621,36 @@ static int finishpcallk (lua_State *L, CallInfo *ci) { | |||
| 615 | 621 | ||
| 616 | /* | 622 | /* |
| 617 | ** Completes the execution of a C function interrupted by an yield. | 623 | ** Completes the execution of a C function interrupted by an yield. |
| 618 | ** The interruption must have happened while the function was | 624 | ** The interruption must have happened while the function was either |
| 619 | ** executing 'lua_callk' or 'lua_pcallk'. In the second case, the | 625 | ** closing its tbc variables in 'moveresults' or executing |
| 620 | ** call to 'finishpcallk' finishes the interrupted execution of | 626 | ** 'lua_callk'/'lua_pcallk'. In the first case, it just redoes |
| 621 | ** 'lua_pcallk'. After that, it calls the continuation of the | 627 | ** 'luaD_poscall'. In the second case, the call to 'finishpcallk' |
| 622 | ** interrupted function and finally it completes the job of the | 628 | ** finishes the interrupted execution of 'lua_pcallk'. After that, it |
| 623 | ** 'luaD_call' that called the function. | 629 | ** calls the continuation of the interrupted function and finally it |
| 624 | ** In the call to 'adjustresults', we do not know the number of | 630 | ** completes the job of the 'luaD_call' that called the function. In |
| 625 | ** results of the function called by 'lua_callk'/'lua_pcallk', | 631 | ** the call to 'adjustresults', we do not know the number of results |
| 626 | ** so we are conservative and use LUA_MULTRET (always adjust). | 632 | ** of the function called by 'lua_callk'/'lua_pcallk', so we are |
| 633 | ** conservative and use LUA_MULTRET (always adjust). | ||
| 627 | */ | 634 | */ |
| 628 | static void finishCcall (lua_State *L, CallInfo *ci) { | 635 | static void finishCcall (lua_State *L, CallInfo *ci) { |
| 629 | int n; | 636 | int n; /* actual number of results from C function */ |
| 630 | int status = LUA_YIELD; /* default if there were no errors */ | 637 | if (ci->callstatus & CIST_CLSRET) { /* was returning? */ |
| 631 | /* must have a continuation and must be able to call it */ | 638 | lua_assert(hastocloseCfunc(ci->nresults)); |
| 632 | lua_assert(ci->u.c.k != NULL && yieldable(L)); | 639 | n = ci->u2.nres; /* just redo 'luaD_poscall' */ |
| 633 | if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ | 640 | /* don't need to reset CIST_CLSRET, as it will be set again anyway */ |
| 634 | status = finishpcallk(L, ci); /* finish it */ | 641 | } |
| 635 | adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ | 642 | else { |
| 636 | lua_unlock(L); | 643 | int status = LUA_YIELD; /* default if there were no errors */ |
| 637 | n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ | 644 | /* must have a continuation and must be able to call it */ |
| 638 | lua_lock(L); | 645 | lua_assert(ci->u.c.k != NULL && yieldable(L)); |
| 639 | api_checknelems(L, n); | 646 | if (ci->callstatus & CIST_YPCALL) /* was inside a 'lua_pcallk'? */ |
| 647 | status = finishpcallk(L, ci); /* finish it */ | ||
| 648 | adjustresults(L, LUA_MULTRET); /* finish 'lua_callk' */ | ||
| 649 | lua_unlock(L); | ||
| 650 | n = (*ci->u.c.k)(L, status, ci->u.c.ctx); /* call continuation */ | ||
| 651 | lua_lock(L); | ||
| 652 | api_checknelems(L, n); | ||
| 653 | } | ||
| 640 | luaD_poscall(L, ci, n); /* finish 'luaD_call' */ | 654 | luaD_poscall(L, ci, n); /* finish 'luaD_call' */ |
| 641 | } | 655 | } |
| 642 | 656 | ||
| @@ -164,6 +164,8 @@ typedef struct stringtable { | |||
| 164 | ** protected call; | 164 | ** protected call; |
| 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 | ||
| 168 | ** returning from a C function; | ||
| 167 | ** - field 'transferinfo' is used only during call/returnhooks, | 169 | ** - field 'transferinfo' is used only during call/returnhooks, |
| 168 | ** before the function starts or after it ends. | 170 | ** before the function starts or after it ends. |
| 169 | */ | 171 | */ |
| @@ -186,6 +188,7 @@ typedef struct CallInfo { | |||
| 186 | union { | 188 | union { |
| 187 | int funcidx; /* called-function index */ | 189 | int funcidx; /* called-function index */ |
| 188 | int nyield; /* number of values yielded */ | 190 | int nyield; /* number of values yielded */ |
| 191 | int nres; /* number of values returned */ | ||
| 189 | struct { /* info about transferred values (for call/return hooks) */ | 192 | struct { /* info about transferred values (for call/return hooks) */ |
| 190 | unsigned short ftransfer; /* offset of first value transferred */ | 193 | unsigned short ftransfer; /* offset of first value transferred */ |
| 191 | unsigned short ntransfer; /* number of values transferred */ | 194 | unsigned short ntransfer; /* number of values transferred */ |
| @@ -203,15 +206,16 @@ typedef struct CallInfo { | |||
| 203 | #define CIST_C (1<<1) /* call is running a C function */ | 206 | #define CIST_C (1<<1) /* call is running a C function */ |
| 204 | #define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ | 207 | #define CIST_FRESH (1<<2) /* call is on a fresh "luaV_execute" frame */ |
| 205 | #define CIST_HOOKED (1<<3) /* call is running a debug hook */ | 208 | #define CIST_HOOKED (1<<3) /* call is running a debug hook */ |
| 206 | #define CIST_YPCALL (1<<4) /* call is a yieldable protected call */ | 209 | #define CIST_YPCALL (1<<4) /* doing a yieldable protected call */ |
| 207 | #define CIST_TAIL (1<<5) /* call was tail called */ | 210 | #define CIST_TAIL (1<<5) /* call was tail called */ |
| 208 | #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ | 211 | #define CIST_HOOKYIELD (1<<6) /* last hook called yielded */ |
| 209 | #define CIST_FIN (1<<7) /* call is running a finalizer */ | 212 | #define CIST_FIN (1<<7) /* call is running a finalizer */ |
| 210 | #define CIST_TRAN (1<<8) /* 'ci' has transfer information */ | 213 | #define CIST_TRAN (1<<8) /* 'ci' has transfer information */ |
| 211 | /* Bits 9-11 are used for CIST_RECST (see below) */ | 214 | #define CIST_CLSRET (1<<9) /* function is closing tbc variables */ |
| 212 | #define CIST_RECST 9 | 215 | /* Bits 10-12 are used for CIST_RECST (see below) */ |
| 216 | #define CIST_RECST 10 | ||
| 213 | #if defined(LUA_COMPAT_LT_LE) | 217 | #if defined(LUA_COMPAT_LT_LE) |
| 214 | #define CIST_LEQ (1<<12) /* using __lt for __le */ | 218 | #define CIST_LEQ (1<<13) /* using __lt for __le */ |
| 215 | #endif | 219 | #endif |
| 216 | 220 | ||
| 217 | 221 | ||
diff --git a/manual/manual.of b/manual/manual.of index 89069281..e7040b2b 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
| @@ -3102,6 +3102,9 @@ Close the to-be-closed slot at the given index and set its value to @nil. | |||
| 3102 | The index must be the last index previously marked to be closed | 3102 | The index must be the last index previously marked to be closed |
| 3103 | @see{lua_toclose} that is still active (that is, not closed yet). | 3103 | @see{lua_toclose} that is still active (that is, not closed yet). |
| 3104 | 3104 | ||
| 3105 | A @Lid{__close} metamethod cannot yield | ||
| 3106 | when called through this function. | ||
| 3107 | |||
| 3105 | (Exceptionally, this function was introduced in release 5.4.3. | 3108 | (Exceptionally, this function was introduced in release 5.4.3. |
| 3106 | It is not present in previous 5.4 releases.) | 3109 | It is not present in previous 5.4 releases.) |
| 3107 | 3110 | ||
diff --git a/testes/locals.lua b/testes/locals.lua index 446ec13a..a93839db 100644 --- a/testes/locals.lua +++ b/testes/locals.lua | |||
| @@ -707,7 +707,6 @@ if rawget(_G, "T") then | |||
| 707 | -- results are correct | 707 | -- results are correct |
| 708 | checktable(t, {10, 20}) | 708 | checktable(t, {10, 20}) |
| 709 | end | 709 | end |
| 710 | |||
| 711 | end | 710 | end |
| 712 | 711 | ||
| 713 | 712 | ||
| @@ -930,6 +929,81 @@ assert(co == nil) -- eventually it will be collected | |||
| 930 | collectgarbage() | 929 | collectgarbage() |
| 931 | 930 | ||
| 932 | 931 | ||
| 932 | if rawget(_G, "T") then | ||
| 933 | print("to-be-closed variables x coroutines in C") | ||
| 934 | do | ||
| 935 | local token = 0 | ||
| 936 | local count = 0 | ||
| 937 | local f = T.makeCfunc[[ | ||
| 938 | toclose 1 | ||
| 939 | toclose 2 | ||
| 940 | return . | ||
| 941 | ]] | ||
| 942 | |||
| 943 | local obj = func2close(function (_, msg) | ||
| 944 | count = count + 1 | ||
| 945 | token = coroutine.yield(count, token) | ||
| 946 | end) | ||
| 947 | |||
| 948 | local co = coroutine.wrap(f) | ||
| 949 | local ct, res = co(obj, obj, 10, 20, 30, 3) -- will return 10, 20, 30 | ||
| 950 | -- initial token value, after closing 2nd obj | ||
| 951 | assert(ct == 1 and res == 0) | ||
| 952 | -- run until yield when closing 1st obj | ||
| 953 | ct, res = co(100) | ||
| 954 | assert(ct == 2 and res == 100) | ||
| 955 | res = {co(200)} -- run until end | ||
| 956 | assert(res[1] == 10 and res[2] == 20 and res[3] == 30 and res[4] == nil) | ||
| 957 | assert(token == 200) | ||
| 958 | end | ||
| 959 | |||
| 960 | do | ||
| 961 | local f = T.makeCfunc[[ | ||
| 962 | toclose 1 | ||
| 963 | return . | ||
| 964 | ]] | ||
| 965 | |||
| 966 | local obj = func2close(function () | ||
| 967 | local temp | ||
| 968 | local x <close> = func2close(function () | ||
| 969 | coroutine.yield(temp) | ||
| 970 | return 1,2,3 -- to be ignored | ||
| 971 | end) | ||
| 972 | temp = coroutine.yield("closing obj") | ||
| 973 | return 1,2,3 -- to be ignored | ||
| 974 | end) | ||
| 975 | |||
| 976 | local co = coroutine.wrap(f) | ||
| 977 | local res = co(obj, 10, 30, 1) -- will return only 30 | ||
| 978 | assert(res == "closing obj") | ||
| 979 | res = co("closing x") | ||
| 980 | assert(res == "closing x") | ||
| 981 | res = {co()} | ||
| 982 | assert(res[1] == 30 and res[2] == nil) | ||
| 983 | end | ||
| 984 | |||
| 985 | do | ||
| 986 | -- still cannot yield inside 'closeslot' | ||
| 987 | local f = T.makeCfunc[[ | ||
| 988 | toclose 1 | ||
| 989 | closeslot 1 | ||
| 990 | ]] | ||
| 991 | local obj = func2close(coroutine.yield) | ||
| 992 | local co = coroutine.create(f) | ||
| 993 | local st, msg = coroutine.resume(co, obj) | ||
| 994 | assert(not st and string.find(msg, "attempt to yield across")) | ||
| 995 | |||
| 996 | -- nor outside a coroutine | ||
| 997 | local f = T.makeCfunc[[ | ||
| 998 | toclose 1 | ||
| 999 | ]] | ||
| 1000 | local st, msg = pcall(f, obj) | ||
| 1001 | assert(not st and string.find(msg, "attempt to yield from outside")) | ||
| 1002 | end | ||
| 1003 | end | ||
| 1004 | |||
| 1005 | |||
| 1006 | |||
| 933 | -- to-be-closed variables in generic for loops | 1007 | -- to-be-closed variables in generic for loops |
| 934 | do | 1008 | do |
| 935 | local numopen = 0 | 1009 | local numopen = 0 |
