aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-26 16:53:51 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-26 16:53:51 -0300
commit58aa09a0b91cf81779d6710d7f9d855bb9d3712f (patch)
tree56adab5aab1307791b7e485677a41203d7369a6c
parent1f81baffadad9d955b030a1a29b9b06042a66552 (diff)
downloadlua-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.c17
-rw-r--r--testes/coroutine.lua22
2 files changed, 30 insertions, 9 deletions
diff --git a/ldo.c b/ldo.c
index 9076c0ed..80c79803 100644
--- a/ldo.c
+++ b/ldo.c
@@ -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) {
349static void rethook (lua_State *L, CallInfo *ci, int nres) { 350static 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'