diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-26 16:53:51 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-26 16:53:51 -0300 |
| commit | 58aa09a0b91cf81779d6710d7f9d855bb9d3712f (patch) | |
| tree | 56adab5aab1307791b7e485677a41203d7369a6c | |
| parent | 1f81baffadad9d955b030a1a29b9b06042a66552 (diff) | |
| download | lua-58aa09a0b91cf81779d6710d7f9d855bb9d3712f.tar.gz lua-58aa09a0b91cf81779d6710d7f9d855bb9d3712f.tar.bz2 lua-58aa09a0b91cf81779d6710d7f9d855bb9d3712f.zip | |
Small improvements in hooks
- 'L->top' is set once in 'luaD_hook', instead of being set in
'luaD_hookcall' and 'rethook';
- resume discard arguments when returning after an yield inside a hook
(arguments may interfere with the coroutine stack);
- yield inside a hook asserts it has no arguments.
| -rw-r--r-- | ldo.c | 17 | ||||
| -rw-r--r-- | testes/coroutine.lua | 22 |
2 files changed, 30 insertions, 9 deletions
| @@ -306,6 +306,8 @@ void luaD_hook (lua_State *L, int event, int line, | |||
| 306 | ci->u2.transferinfo.ftransfer = ftransfer; | 306 | ci->u2.transferinfo.ftransfer = ftransfer; |
| 307 | ci->u2.transferinfo.ntransfer = ntransfer; | 307 | ci->u2.transferinfo.ntransfer = ntransfer; |
| 308 | } | 308 | } |
| 309 | if (isLua(ci) && L->top < ci->top) | ||
| 310 | L->top = ci->top; /* protect entire activation register */ | ||
| 309 | luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ | 311 | luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ |
| 310 | if (ci->top < L->top + LUA_MINSTACK) | 312 | if (ci->top < L->top + LUA_MINSTACK) |
| 311 | ci->top = L->top + LUA_MINSTACK; | 313 | ci->top = L->top + LUA_MINSTACK; |
| @@ -333,7 +335,6 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) { | |||
| 333 | int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL | 335 | int event = (ci->callstatus & CIST_TAIL) ? LUA_HOOKTAILCALL |
| 334 | : LUA_HOOKCALL; | 336 | : LUA_HOOKCALL; |
| 335 | Proto *p = ci_func(ci)->p; | 337 | Proto *p = ci_func(ci)->p; |
| 336 | L->top = ci->top; /* prepare top */ | ||
| 337 | ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ | 338 | ci->u.l.savedpc++; /* hooks assume 'pc' is already incremented */ |
| 338 | luaD_hook(L, event, -1, 1, p->numparams); | 339 | luaD_hook(L, event, -1, 1, p->numparams); |
| 339 | ci->u.l.savedpc--; /* correct 'pc' */ | 340 | ci->u.l.savedpc--; /* correct 'pc' */ |
| @@ -349,21 +350,17 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) { | |||
| 349 | static void rethook (lua_State *L, CallInfo *ci, int nres) { | 350 | static void rethook (lua_State *L, CallInfo *ci, int nres) { |
| 350 | if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ | 351 | if (L->hookmask & LUA_MASKRET) { /* is return hook on? */ |
| 351 | StkId firstres = L->top - nres; /* index of first result */ | 352 | StkId firstres = L->top - nres; /* index of first result */ |
| 352 | ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ | ||
| 353 | int delta = 0; /* correction for vararg functions */ | 353 | int delta = 0; /* correction for vararg functions */ |
| 354 | int ftransfer; | 354 | int ftransfer; |
| 355 | if (isLuacode(ci)) { | 355 | if (isLua(ci)) { |
| 356 | Proto *p = ci_func(ci)->p; | 356 | Proto *p = ci_func(ci)->p; |
| 357 | if (p->is_vararg) | 357 | if (p->is_vararg) |
| 358 | delta = ci->u.l.nextraargs + p->numparams + 1; | 358 | delta = ci->u.l.nextraargs + p->numparams + 1; |
| 359 | if (L->top < ci->top) | ||
| 360 | L->top = ci->top; /* correct top to run hook */ | ||
| 361 | } | 359 | } |
| 362 | ci->func += delta; /* if vararg, back to virtual 'func' */ | 360 | ci->func += delta; /* if vararg, back to virtual 'func' */ |
| 363 | ftransfer = cast(unsigned short, firstres - ci->func); | 361 | ftransfer = cast(unsigned short, firstres - ci->func); |
| 364 | luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ | 362 | luaD_hook(L, LUA_HOOKRET, -1, ftransfer, nres); /* call it */ |
| 365 | ci->func -= delta; | 363 | ci->func -= delta; |
| 366 | L->top = restorestack(L, oldtop); | ||
| 367 | } | 364 | } |
| 368 | if (isLua(ci = ci->previous)) | 365 | if (isLua(ci = ci->previous)) |
| 369 | L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */ | 366 | L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */ |
| @@ -707,8 +704,10 @@ static void resume (lua_State *L, void *ud) { | |||
| 707 | lua_assert(L->status == LUA_YIELD); | 704 | lua_assert(L->status == LUA_YIELD); |
| 708 | L->status = LUA_OK; /* mark that it is running (again) */ | 705 | L->status = LUA_OK; /* mark that it is running (again) */ |
| 709 | luaE_incCstack(L); /* control the C stack */ | 706 | luaE_incCstack(L); /* control the C stack */ |
| 710 | if (isLua(ci)) /* yielded inside a hook? */ | 707 | if (isLua(ci)) { /* yielded inside a hook? */ |
| 708 | L->top = firstArg; /* discard arguments */ | ||
| 711 | luaV_execute(L, ci); /* just continue running Lua code */ | 709 | luaV_execute(L, ci); /* just continue running Lua code */ |
| 710 | } | ||
| 712 | else { /* 'common' yield */ | 711 | else { /* 'common' yield */ |
| 713 | if (ci->u.c.k != NULL) { /* does it have a continuation function? */ | 712 | if (ci->u.c.k != NULL) { /* does it have a continuation function? */ |
| 714 | lua_unlock(L); | 713 | lua_unlock(L); |
| @@ -793,15 +792,15 @@ LUA_API int lua_yieldk (lua_State *L, int nresults, lua_KContext ctx, | |||
| 793 | luaG_runerror(L, "attempt to yield from outside a coroutine"); | 792 | luaG_runerror(L, "attempt to yield from outside a coroutine"); |
| 794 | } | 793 | } |
| 795 | L->status = LUA_YIELD; | 794 | L->status = LUA_YIELD; |
| 795 | ci->u2.nyield = nresults; /* save number of results */ | ||
| 796 | if (isLua(ci)) { /* inside a hook? */ | 796 | if (isLua(ci)) { /* inside a hook? */ |
| 797 | lua_assert(!isLuacode(ci)); | 797 | lua_assert(!isLuacode(ci)); |
| 798 | api_check(L, nresults == 0, "hooks cannot yield values"); | ||
| 798 | api_check(L, k == NULL, "hooks cannot continue after yielding"); | 799 | api_check(L, k == NULL, "hooks cannot continue after yielding"); |
| 799 | ci->u2.nyield = 0; /* no results */ | ||
| 800 | } | 800 | } |
| 801 | else { | 801 | else { |
| 802 | if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ | 802 | if ((ci->u.c.k = k) != NULL) /* is there a continuation? */ |
| 803 | ci->u.c.ctx = ctx; /* save context */ | 803 | ci->u.c.ctx = ctx; /* save context */ |
| 804 | ci->u2.nyield = nresults; /* save number of results */ | ||
| 805 | luaD_throw(L, LUA_YIELD); | 804 | luaD_throw(L, LUA_YIELD); |
| 806 | } | 805 | } |
| 807 | lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ | 806 | lua_assert(ci->callstatus & CIST_HOOKED); /* must be inside a hook */ |
diff --git a/testes/coroutine.lua b/testes/coroutine.lua index fbeabd07..b36b76ea 100644 --- a/testes/coroutine.lua +++ b/testes/coroutine.lua | |||
| @@ -498,6 +498,28 @@ else | |||
| 498 | 498 | ||
| 499 | assert(B // A == 7) -- fact(7) // fact(6) | 499 | assert(B // A == 7) -- fact(7) // fact(6) |
| 500 | 500 | ||
| 501 | do -- hooks vs. multiple values | ||
| 502 | local done | ||
| 503 | local function test (n) | ||
| 504 | done = false | ||
| 505 | return coroutine.wrap(function () | ||
| 506 | local a = {} | ||
| 507 | for i = 1, n do a[i] = i end | ||
| 508 | -- 'pushint' just to perturb the stack | ||
| 509 | T.sethook("pushint 10; yield 0", "", 1) -- yield at each op. | ||
| 510 | local a1 = {table.unpack(a)} -- must keep top between ops. | ||
| 511 | assert(#a1 == n) | ||
| 512 | for i = 1, n do assert(a[i] == i) end | ||
| 513 | done = true | ||
| 514 | end) | ||
| 515 | end | ||
| 516 | -- arguments to the coroutine are just to perturb its stack | ||
| 517 | local co = test(0); while not done do co(30) end | ||
| 518 | co = test(1); while not done do co(20, 10) end | ||
| 519 | co = test(3); while not done do co() end | ||
| 520 | co = test(100); while not done do co() end | ||
| 521 | end | ||
| 522 | |||
| 501 | local line = debug.getinfo(1, "l").currentline + 2 -- get line number | 523 | local line = debug.getinfo(1, "l").currentline + 2 -- get line number |
| 502 | local function foo () | 524 | local function foo () |
| 503 | local x = 10 --<< this line is 'line' | 525 | local x = 10 --<< this line is 'line' |
