diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-02-12 13:36:30 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-02-12 13:36:30 -0300 |
commit | bc970005ce2e258e29a5c315ea4e49f76a66586e (patch) | |
tree | 5575ca66aeddad2036df74f38fa2bed217c2801e /ldo.c | |
parent | f79ccdca9bbe9d486d91a44a4464b99ce38de0e2 (diff) | |
download | lua-bc970005ce2e258e29a5c315ea4e49f76a66586e.tar.gz lua-bc970005ce2e258e29a5c315ea4e49f76a66586e.tar.bz2 lua-bc970005ce2e258e29a5c315ea4e49f76a66586e.zip |
'__close' methods can yield in the return of a C function
When, inside a coroutine, a C function with to-be-closed slots return,
the corresponding metamethods can yield. ('__close' metamethods called
through 'lua_closeslot' still cannot yield, as there is no continuation
to go when resuming.)
Diffstat (limited to 'ldo.c')
-rw-r--r-- | ldo.c | 72 |
1 files changed, 43 insertions, 29 deletions
@@ -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 | ||