diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-01-28 11:45:45 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2025-01-28 11:45:45 -0300 |
| commit | 39a14ea7d7b14172595c61619e8f35c2614b2606 (patch) | |
| tree | 863ea56e6014de9e1284f2e47ac1fbd4b69339ed | |
| parent | c4e7cdb541d89142056927ebdfd8f97017d38f45 (diff) | |
| download | lua-39a14ea7d7b14172595c61619e8f35c2614b2606.tar.gz lua-39a14ea7d7b14172595c61619e8f35c2614b2606.tar.bz2 lua-39a14ea7d7b14172595c61619e8f35c2614b2606.zip | |
CallInfo bit CIST_CLSRET broken in two
Since commit f407b3c4a, it was being used for two distinct (and
incompatible) meanings:
A: Function has TBC variables (now bit CIST_TBC)
B: Interpreter is closing TBC variables (original bit CIST_CLSRET)
B implies A, but A does not imply B.
| -rw-r--r-- | lapi.c | 6 | ||||
| -rw-r--r-- | ldo.c | 12 | ||||
| -rw-r--r-- | lstate.h | 4 | ||||
| -rw-r--r-- | testes/coroutine.lua | 37 |
4 files changed, 44 insertions, 15 deletions
| @@ -195,7 +195,7 @@ LUA_API void lua_settop (lua_State *L, int idx) { | |||
| 195 | } | 195 | } |
| 196 | newtop = L->top.p + diff; | 196 | newtop = L->top.p + diff; |
| 197 | if (diff < 0 && L->tbclist.p >= newtop) { | 197 | if (diff < 0 && L->tbclist.p >= newtop) { |
| 198 | lua_assert(ci->callstatus & CIST_CLSRET); | 198 | lua_assert(ci->callstatus & CIST_TBC); |
| 199 | newtop = luaF_close(L, newtop, CLOSEKTOP, 0); | 199 | newtop = luaF_close(L, newtop, CLOSEKTOP, 0); |
| 200 | } | 200 | } |
| 201 | L->top.p = newtop; /* correct top only after closing any upvalue */ | 201 | L->top.p = newtop; /* correct top only after closing any upvalue */ |
| @@ -207,7 +207,7 @@ LUA_API void lua_closeslot (lua_State *L, int idx) { | |||
| 207 | StkId level; | 207 | StkId level; |
| 208 | lua_lock(L); | 208 | lua_lock(L); |
| 209 | level = index2stack(L, idx); | 209 | level = index2stack(L, idx); |
| 210 | api_check(L, (L->ci->callstatus & CIST_CLSRET) && L->tbclist.p == level, | 210 | api_check(L, (L->ci->callstatus & CIST_TBC) && (L->tbclist.p == level), |
| 211 | "no variable to close at given level"); | 211 | "no variable to close at given level"); |
| 212 | level = luaF_close(L, level, CLOSEKTOP, 0); | 212 | level = luaF_close(L, level, CLOSEKTOP, 0); |
| 213 | setnilvalue(s2v(level)); | 213 | setnilvalue(s2v(level)); |
| @@ -1280,7 +1280,7 @@ LUA_API void lua_toclose (lua_State *L, int idx) { | |||
| 1280 | o = index2stack(L, idx); | 1280 | o = index2stack(L, idx); |
| 1281 | api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); | 1281 | api_check(L, L->tbclist.p < o, "given index below or equal a marked one"); |
| 1282 | luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ | 1282 | luaF_newtbcupval(L, o); /* create new to-be-closed upvalue */ |
| 1283 | L->ci->callstatus |= CIST_CLSRET; /* mark that function has TBC slots */ | 1283 | L->ci->callstatus |= CIST_TBC; /* mark that function has TBC slots */ |
| 1284 | lua_unlock(L); | 1284 | lua_unlock(L); |
| 1285 | } | 1285 | } |
| 1286 | 1286 | ||
| @@ -505,7 +505,7 @@ l_sinline void genmoveresults (lua_State *L, StkId res, int nres, | |||
| 505 | ** Given 'nres' results at 'firstResult', move 'fwanted-1' of them | 505 | ** Given 'nres' results at 'firstResult', move 'fwanted-1' of them |
| 506 | ** to 'res'. Handle most typical cases (zero results for commands, | 506 | ** to 'res'. Handle most typical cases (zero results for commands, |
| 507 | ** one result for expressions, multiple results for tail calls/single | 507 | ** one result for expressions, multiple results for tail calls/single |
| 508 | ** parameters) separated. The flag CIST_CLSRET in 'fwanted', if set, | 508 | ** parameters) separated. The flag CIST_TBC in 'fwanted', if set, |
| 509 | ** forces the swicth to go to the default case. | 509 | ** forces the swicth to go to the default case. |
| 510 | */ | 510 | */ |
| 511 | l_sinline void moveresults (lua_State *L, StkId res, int nres, | 511 | l_sinline void moveresults (lua_State *L, StkId res, int nres, |
| @@ -526,8 +526,9 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, | |||
| 526 | break; | 526 | break; |
| 527 | default: { /* two/more results and/or to-be-closed variables */ | 527 | default: { /* two/more results and/or to-be-closed variables */ |
| 528 | int wanted = get_nresults(fwanted); | 528 | int wanted = get_nresults(fwanted); |
| 529 | if (fwanted & CIST_CLSRET) { /* to-be-closed variables? */ | 529 | if (fwanted & CIST_TBC) { /* to-be-closed variables? */ |
| 530 | L->ci->u2.nres = nres; | 530 | L->ci->u2.nres = nres; |
| 531 | L->ci->callstatus |= CIST_CLSRET; /* in case of yields */ | ||
| 531 | res = luaF_close(L, res, CLOSEKTOP, 1); | 532 | res = luaF_close(L, res, CLOSEKTOP, 1); |
| 532 | L->ci->callstatus &= ~CIST_CLSRET; | 533 | L->ci->callstatus &= ~CIST_CLSRET; |
| 533 | if (L->hookmask) { /* if needed, call hook after '__close's */ | 534 | if (L->hookmask) { /* if needed, call hook after '__close's */ |
| @@ -552,8 +553,8 @@ l_sinline void moveresults (lua_State *L, StkId res, int nres, | |||
| 552 | ** that. | 553 | ** that. |
| 553 | */ | 554 | */ |
| 554 | void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | 555 | void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { |
| 555 | l_uint32 fwanted = ci->callstatus & (CIST_CLSRET | CIST_NRESULTS); | 556 | l_uint32 fwanted = ci->callstatus & (CIST_TBC | CIST_NRESULTS); |
| 556 | if (l_unlikely(L->hookmask) && !(fwanted & CIST_CLSRET)) | 557 | if (l_unlikely(L->hookmask) && !(fwanted & CIST_TBC)) |
| 557 | rethook(L, ci, nres); | 558 | rethook(L, ci, nres); |
| 558 | /* move results to proper place */ | 559 | /* move results to proper place */ |
| 559 | moveresults(L, ci->func.p, nres, fwanted); | 560 | moveresults(L, ci->func.p, nres, fwanted); |
| @@ -785,7 +786,8 @@ static int finishpcallk (lua_State *L, CallInfo *ci) { | |||
| 785 | */ | 786 | */ |
| 786 | static void finishCcall (lua_State *L, CallInfo *ci) { | 787 | static void finishCcall (lua_State *L, CallInfo *ci) { |
| 787 | int n; /* actual number of results from C function */ | 788 | int n; /* actual number of results from C function */ |
| 788 | if (ci->callstatus & CIST_CLSRET) { /* was returning? */ | 789 | if (ci->callstatus & CIST_CLSRET) { /* was closing TBC variable? */ |
| 790 | lua_assert(ci->callstatus & CIST_TBC); | ||
| 789 | n = ci->u2.nres; /* just redo 'luaD_poscall' */ | 791 | n = ci->u2.nres; /* just redo 'luaD_poscall' */ |
| 790 | /* don't need to reset CIST_CLSRET, as it will be set again anyway */ | 792 | /* don't need to reset CIST_CLSRET, as it will be set again anyway */ |
| 791 | } | 793 | } |
| @@ -235,8 +235,10 @@ struct CallInfo { | |||
| 235 | #define CIST_FRESH cast(l_uint32, CIST_C << 1) | 235 | #define CIST_FRESH cast(l_uint32, CIST_C << 1) |
| 236 | /* function is closing tbc variables */ | 236 | /* function is closing tbc variables */ |
| 237 | #define CIST_CLSRET (CIST_FRESH << 1) | 237 | #define CIST_CLSRET (CIST_FRESH << 1) |
| 238 | /* function has tbc variables to close */ | ||
| 239 | #define CIST_TBC (CIST_CLSRET << 1) | ||
| 238 | /* original value of 'allowhook' */ | 240 | /* original value of 'allowhook' */ |
| 239 | #define CIST_OAH (CIST_CLSRET << 1) | 241 | #define CIST_OAH (CIST_TBC << 1) |
| 240 | /* call is running a debug hook */ | 242 | /* call is running a debug hook */ |
| 241 | #define CIST_HOOKED (CIST_OAH << 1) | 243 | #define CIST_HOOKED (CIST_OAH << 1) |
| 242 | /* doing a yieldable protected call */ | 244 | /* doing a yieldable protected call */ |
diff --git a/testes/coroutine.lua b/testes/coroutine.lua index c1252ab8..78b9bdca 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua | |||
| @@ -515,7 +515,7 @@ else | |||
| 515 | print "testing yields inside hooks" | 515 | print "testing yields inside hooks" |
| 516 | 516 | ||
| 517 | local turn | 517 | local turn |
| 518 | 518 | ||
| 519 | local function fact (t, x) | 519 | local function fact (t, x) |
| 520 | assert(turn == t) | 520 | assert(turn == t) |
| 521 | if x == 0 then return 1 | 521 | if x == 0 then return 1 |
| @@ -642,7 +642,7 @@ else | |||
| 642 | 642 | ||
| 643 | 643 | ||
| 644 | print "testing coroutine API" | 644 | print "testing coroutine API" |
| 645 | 645 | ||
| 646 | -- reusing a thread | 646 | -- reusing a thread |
| 647 | assert(T.testC([[ | 647 | assert(T.testC([[ |
| 648 | newthread # create thread | 648 | newthread # create thread |
| @@ -920,7 +920,7 @@ do -- a few more tests for comparison operators | |||
| 920 | until res ~= 10 | 920 | until res ~= 10 |
| 921 | return res | 921 | return res |
| 922 | end | 922 | end |
| 923 | 923 | ||
| 924 | local function test () | 924 | local function test () |
| 925 | local a1 = setmetatable({x=1}, mt1) | 925 | local a1 = setmetatable({x=1}, mt1) |
| 926 | local a2 = setmetatable({x=2}, mt2) | 926 | local a2 = setmetatable({x=2}, mt2) |
| @@ -932,7 +932,7 @@ do -- a few more tests for comparison operators | |||
| 932 | assert(2 >= a2) | 932 | assert(2 >= a2) |
| 933 | return true | 933 | return true |
| 934 | end | 934 | end |
| 935 | 935 | ||
| 936 | run(test) | 936 | run(test) |
| 937 | 937 | ||
| 938 | end | 938 | end |
| @@ -1037,6 +1037,31 @@ f = T.makeCfunc([[ | |||
| 1037 | return * | 1037 | return * |
| 1038 | ]], 23, "huu") | 1038 | ]], 23, "huu") |
| 1039 | 1039 | ||
| 1040 | |||
| 1041 | do -- testing bug introduced in commit f407b3c4a | ||
| 1042 | local X = false -- flag "to be closed" | ||
| 1043 | local coro = coroutine.wrap(T.testC) | ||
| 1044 | -- runs it until 'pcallk' (that yields) | ||
| 1045 | -- 4th argument (at index 4): object to be closed | ||
| 1046 | local res1, res2 = coro( | ||
| 1047 | [[ | ||
| 1048 | toclose 3 # this could break the next 'pcallk' | ||
| 1049 | pushvalue 2 # push function 'yield' to call it | ||
| 1050 | pushint 22; pushint 33 # arguments to yield | ||
| 1051 | # calls 'yield' (2 args; 2 results; continuation function at index 4) | ||
| 1052 | pcallk 2 2 4 | ||
| 1053 | invalid command (should not arrive here) | ||
| 1054 | ]], -- 1st argument (at index 1): code; | ||
| 1055 | coroutine.yield, -- (at index 2): function to be called | ||
| 1056 | func2close(function () X = true end), -- (index 3): TBC slot | ||
| 1057 | "pushint 43; return 3" -- (index 4): code for continuation function | ||
| 1058 | ) | ||
| 1059 | |||
| 1060 | assert(res1 == 22 and res2 == 33 and not X) | ||
| 1061 | local res1, res2, res3 = coro(34, "hi") -- runs continuation function | ||
| 1062 | assert(res1 == 34 and res2 == "hi" and res3 == 43 and X) | ||
| 1063 | end | ||
| 1064 | |||
| 1040 | x = coroutine.wrap(f) | 1065 | x = coroutine.wrap(f) |
| 1041 | assert(x() == 102) | 1066 | assert(x() == 102) |
| 1042 | eqtab({x()}, {23, "huu"}) | 1067 | eqtab({x()}, {23, "huu"}) |
| @@ -1094,11 +1119,11 @@ co = coroutine.wrap(function (...) return | |||
| 1094 | cannot be here! | 1119 | cannot be here! |
| 1095 | ]], | 1120 | ]], |
| 1096 | [[ # 1st continuation | 1121 | [[ # 1st continuation |
| 1097 | yieldk 0 3 | 1122 | yieldk 0 3 |
| 1098 | cannot be here! | 1123 | cannot be here! |
| 1099 | ]], | 1124 | ]], |
| 1100 | [[ # 2nd continuation | 1125 | [[ # 2nd continuation |
| 1101 | yieldk 0 4 | 1126 | yieldk 0 4 |
| 1102 | cannot be here! | 1127 | cannot be here! |
| 1103 | ]], | 1128 | ]], |
| 1104 | [[ # 3th continuation | 1129 | [[ # 3th continuation |
