diff options
| -rw-r--r-- | lapi.c | 8 | ||||
| -rw-r--r-- | lapi.h | 16 | ||||
| -rw-r--r-- | ldo.c | 20 | ||||
| -rw-r--r-- | testes/api.lua | 17 |
4 files changed, 31 insertions, 30 deletions
| @@ -207,7 +207,7 @@ LUA_API void lua_settop (lua_State *L, int idx) { | |||
| 207 | } | 207 | } |
| 208 | newtop = L->top.p + diff; | 208 | newtop = L->top.p + diff; |
| 209 | if (diff < 0 && L->tbclist.p >= newtop) { | 209 | if (diff < 0 && L->tbclist.p >= newtop) { |
| 210 | lua_assert(hastocloseCfunc(ci->nresults)); | 210 | lua_assert(ci->callstatus & CIST_CLSRET); |
| 211 | newtop = luaF_close(L, newtop, CLOSEKTOP, 0); | 211 | newtop = luaF_close(L, newtop, CLOSEKTOP, 0); |
| 212 | } | 212 | } |
| 213 | L->top.p = newtop; /* correct top only after closing any upvalue */ | 213 | L->top.p = newtop; /* correct top only after closing any upvalue */ |
| @@ -219,7 +219,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { | |||
| 219 | StkId level; | 219 | StkId level; |
| 220 | lua_lock(L); | 220 | lua_lock(L); |
| 221 | level = index2stack(L, idx); | 221 | level = index2stack(L, idx); |
| 222 | api_check(L, hastocloseCfunc(L->ci->nresults) && L->tbclist.p == level, | 222 | api_check(L, (L->ci->callstatus & CIST_CLSRET) && L->tbclist.p == level, |
| 223 | "no variable to close at given level"); | 223 | "no variable to close at given level"); |
| 224 | level = luaF_close(L, level, CLOSEKTOP, 0); | 224 | level = luaF_close(L, level, CLOSEKTOP, 0); |
| 225 | setnilvalue(s2v(level)); | 225 | setnilvalue(s2v(level)); |
| @@ -1287,9 +1287,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) { | |||
| 1287 | nresults = L->ci->nresults; | 1287 | nresults = L->ci->nresults; |
| 1288 | api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); | 1288 | api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); |
| 1289 | luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ | 1289 | luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ |
| 1290 | if (!hastocloseCfunc(nresults)) /* function not marked yet? */ | 1290 | L->ci->callstatus |= CIST_CLSRET; /* mark that function has TBC slots */ |
| 1291 | L->ci->nresults = codeNresults(nresults); /* mark it */ | ||
| 1292 | lua_assert(hastocloseCfunc(L->ci->nresults)); | ||
| 1293 | lua_unlock(L); | 1291 | lua_unlock(L); |
| 1294 | } | 1292 | } |
| 1295 | 1293 | ||
| @@ -62,20 +62,4 @@ | |||
| 62 | L->tbclist.p < L->top.p - (n), \ | 62 | L->tbclist.p < L->top.p - (n), \ |
| 63 | "not enough free elements in the stack") | 63 | "not enough free elements in the stack") |
| 64 | 64 | ||
| 65 | |||
| 66 | /* | ||
| 67 | ** To reduce the overhead of returning from C functions, the presence of | ||
| 68 | ** to-be-closed variables in these functions is coded in the CallInfo's | ||
| 69 | ** field 'nresults', in a way that functions with no to-be-closed variables | ||
| 70 | ** with zero, one, or "all" wanted results have no overhead. Functions | ||
| 71 | ** with other number of wanted results, as well as functions with | ||
| 72 | ** variables to be closed, have an extra check. | ||
| 73 | */ | ||
| 74 | |||
| 75 | #define hastocloseCfunc(n) ((n) < LUA_MULTRET) | ||
| 76 | |||
| 77 | /* Map [-1, inf) (range of 'nresults') into (-inf, -2] */ | ||
| 78 | #define codeNresults(n) (-(n) - 3) | ||
| 79 | #define decodeNresults(n) (-(n) - 3) | ||
| 80 | |||
| 81 | #endif | 65 | #endif |
| @@ -462,22 +462,23 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { | |||
| 462 | StkId firstresult; | 462 | StkId firstresult; |
| 463 | int i; | 463 | int i; |
| 464 | switch (wanted) { /* handle typical cases separately */ | 464 | switch (wanted) { /* handle typical cases separately */ |
| 465 | case 0: /* no values needed */ | 465 | case 0 + 1: /* no values needed */ |
| 466 | L->top.p = res; | 466 | L->top.p = res; |
| 467 | return; | 467 | return; |
| 468 | case 1: /* one value needed */ | 468 | case 1 + 1: /* one value needed */ |
| 469 | if (nres == 0) /* no results? */ | 469 | if (nres == 0) /* no results? */ |
| 470 | setnilvalue(s2v(res)); /* adjust with nil */ | 470 | setnilvalue(s2v(res)); /* adjust with nil */ |
| 471 | else /* at least one result */ | 471 | else /* at least one result */ |
| 472 | setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ | 472 | setobjs2s(L, res, L->top.p - nres); /* move it to proper place */ |
| 473 | L->top.p = res + 1; | 473 | L->top.p = res + 1; |
| 474 | return; | 474 | return; |
| 475 | case LUA_MULTRET: | 475 | case LUA_MULTRET + 1: |
| 476 | wanted = nres; /* we want all results */ | 476 | wanted = nres; /* we want all results */ |
| 477 | break; | 477 | break; |
| 478 | default: /* two/more results and/or to-be-closed variables */ | 478 | default: /* two/more results and/or to-be-closed variables */ |
| 479 | if (hastocloseCfunc(wanted)) { /* to-be-closed variables? */ | 479 | if (!(wanted & CIST_CLSRET)) |
| 480 | L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ | 480 | wanted--; |
| 481 | else { /* to-be-closed variables? */ | ||
| 481 | L->ci->u2.nres = nres; | 482 | L->ci->u2.nres = nres; |
| 482 | res = luaF_close(L, res, CLOSEKTOP, 1); | 483 | res = luaF_close(L, res, CLOSEKTOP, 1); |
| 483 | L->ci->callstatus &= ~CIST_CLSRET; | 484 | L->ci->callstatus &= ~CIST_CLSRET; |
| @@ -486,7 +487,7 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { | |||
| 486 | rethook(L, L->ci, nres); | 487 | rethook(L, L->ci, nres); |
| 487 | res = restorestack(L, savedres); /* hook can move stack */ | 488 | res = restorestack(L, savedres); /* hook can move stack */ |
| 488 | } | 489 | } |
| 489 | wanted = decodeNresults(wanted); | 490 | wanted = (wanted & ~CIST_CLSRET) - 1; |
| 490 | if (wanted == LUA_MULTRET) | 491 | if (wanted == LUA_MULTRET) |
| 491 | wanted = nres; /* we want all results */ | 492 | wanted = nres; /* we want all results */ |
| 492 | } | 493 | } |
| @@ -511,8 +512,10 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, int wanted) { | |||
| 511 | ** that. | 512 | ** that. |
| 512 | */ | 513 | */ |
| 513 | void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | 514 | void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { |
| 514 | int wanted = ci->nresults; | 515 | int wanted = ci->nresults + 1; |
| 515 | if (l_unlikely(L->hookmask && !hastocloseCfunc(wanted))) | 516 | if (ci->callstatus & CIST_CLSRET) |
| 517 | wanted |= CIST_CLSRET; /* don't check hook in this case */ | ||
| 518 | else if (l_unlikely(L->hookmask)) | ||
| 516 | rethook(L, ci, nres); | 519 | rethook(L, ci, nres); |
| 517 | /* move results to proper place */ | 520 | /* move results to proper place */ |
| 518 | moveresults(L, ci->func.p, nres, wanted); | 521 | moveresults(L, ci->func.p, nres, wanted); |
| @@ -736,7 +739,6 @@ static int finishpcallk (lua_State *L, CallInfo *ci) { | |||
| 736 | static void finishCcall (lua_State *L, CallInfo *ci) { | 739 | static void finishCcall (lua_State *L, CallInfo *ci) { |
| 737 | int n; /* actual number of results from C function */ | 740 | int n; /* actual number of results from C function */ |
| 738 | if (ci->callstatus & CIST_CLSRET) { /* was returning? */ | 741 | if (ci->callstatus & CIST_CLSRET) { /* was returning? */ |
| 739 | lua_assert(hastocloseCfunc(ci->nresults)); | ||
| 740 | n = ci->u2.nres; /* just redo 'luaD_poscall' */ | 742 | n = ci->u2.nres; /* just redo 'luaD_poscall' */ |
| 741 | /* don't need to reset CIST_CLSRET, as it will be set again anyway */ | 743 | /* don't need to reset CIST_CLSRET, as it will be set again anyway */ |
| 742 | } | 744 | } |
diff --git a/testes/api.lua b/testes/api.lua index dc485240..ae2f82dd 100644 --- a/testes/api.lua +++ b/testes/api.lua | |||
| @@ -165,6 +165,23 @@ do -- test returning more results than fit in the caller stack | |||
| 165 | end | 165 | end |
| 166 | 166 | ||
| 167 | 167 | ||
| 168 | do -- testing multipe returns | ||
| 169 | local function foo (n) | ||
| 170 | if n > 0 then return n, foo(n - 1) end | ||
| 171 | end | ||
| 172 | |||
| 173 | local t = {T.testC("call 1 10; return 10", foo, 20)} | ||
| 174 | assert(t[1] == 20 and t[10] == 11 and t[11] == nil) | ||
| 175 | |||
| 176 | local t = table.pack(T.testC("call 1 10; return 10", foo, 2)) | ||
| 177 | assert(t[1] == 2 and t[2] == 1 and t[3] == nil and t.n == 10) | ||
| 178 | |||
| 179 | local t = {T.testC([[ | ||
| 180 | checkstack 300 "error"; call 1 250; return 250]], foo, 250)} | ||
| 181 | assert(t[1] == 250 and t[250] == 1 and t[251] == nil) | ||
| 182 | end | ||
| 183 | |||
| 184 | |||
| 168 | -- testing globals | 185 | -- testing globals |
| 169 | _G.AA = 14; _G.BB = "a31" | 186 | _G.AA = 14; _G.BB = "a31" |
| 170 | local a = {T.testC[[ | 187 | local a = {T.testC[[ |
