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' |