aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-21 10:27:22 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2021-01-21 10:27:22 -0300
commit0e9254dfa03d95c3aa2888cf78e9a30bc88d41bc (patch)
treeca3c956e03f8ecc03eb7ed155c8b6f5e3ebef801
parent6ccd24eff58340c00db2877c4558a63c6b859442 (diff)
downloadlua-0e9254dfa03d95c3aa2888cf78e9a30bc88d41bc.tar.gz
lua-0e9254dfa03d95c3aa2888cf78e9a30bc88d41bc.tar.bz2
lua-0e9254dfa03d95c3aa2888cf78e9a30bc88d41bc.zip
Correct order of return hooks vs. close metamethods
The return hook should be called only after closing variables (which are still part of the function). C functions were calling the hook before the metamethods.
-rw-r--r--ldo.c24
-rw-r--r--testes/locals.lua77
2 files changed, 85 insertions, 16 deletions
diff --git a/ldo.c b/ldo.c
index 9e3d6955..57d4f7d5 100644
--- a/ldo.c
+++ b/ldo.c
@@ -341,7 +341,8 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) {
341} 341}
342 342
343 343
344static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) { 344static void rethook (lua_State *L, CallInfo *ci, int nres) {
345 StkId firstres = L->top - nres; /* index of first result */
345 ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ 346 ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
346 int delta = 0; 347 int delta = 0;
347 if (isLuacode(ci)) { 348 if (isLuacode(ci)) {
@@ -360,7 +361,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
360 } 361 }
361 if (isLua(ci = ci->previous)) 362 if (isLua(ci = ci->previous))
362 L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */ 363 L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
363 return restorestack(L, oldtop); 364 L->top = restorestack(L, oldtop);
364} 365}
365 366
366 367
@@ -397,7 +398,7 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
397 case 1: /* one value needed */ 398 case 1: /* one value needed */
398 if (nres == 0) /* no results? */ 399 if (nres == 0) /* no results? */
399 setnilvalue(s2v(res)); /* adjust with nil */ 400 setnilvalue(s2v(res)); /* adjust with nil */
400 else 401 else /* at least one result */
401 setobjs2s(L, res, L->top - nres); /* move it to proper place */ 402 setobjs2s(L, res, L->top - nres); /* move it to proper place */
402 L->top = res + 1; 403 L->top = res + 1;
403 return; 404 return;
@@ -412,6 +413,8 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
412 wanted = codeNresults(wanted); /* correct value */ 413 wanted = codeNresults(wanted); /* correct value */
413 if (wanted == LUA_MULTRET) 414 if (wanted == LUA_MULTRET)
414 wanted = nres; 415 wanted = nres;
416 if (L->hookmask) /* if needed, call hook after '__close's */
417 rethook(L, L->ci, nres);
415 } 418 }
416 break; 419 break;
417 } 420 }
@@ -426,15 +429,18 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
426 429
427 430
428/* 431/*
429** Finishes a function call: calls hook if necessary, removes CallInfo, 432** Finishes a function call: calls hook if necessary, moves current
430** moves current number of results to proper place. 433** number of results to proper place, and returns to previous call
434** info. If function has to close variables, hook must be called after
435** that.
431*/ 436*/
432void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { 437void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
433 if (L->hookmask) 438 int wanted = ci->nresults;
434 L->top = rethook(L, ci, L->top - nres, nres); 439 if (L->hookmask && !hastocloseCfunc(wanted))
435 L->ci = ci->previous; /* back to caller */ 440 rethook(L, ci, nres);
436 /* move results to proper place */ 441 /* move results to proper place */
437 moveresults(L, ci->func, nres, ci->nresults); 442 moveresults(L, ci->func, nres, wanted);
443 L->ci = ci->previous; /* back to caller (after closing variables) */
438} 444}
439 445
440 446
diff --git a/testes/locals.lua b/testes/locals.lua
index 24a95d18..a25b2b9f 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -521,6 +521,14 @@ do -- tbc inside close methods
521end 521end
522 522
523 523
524local function checktable (t1, t2)
525 assert(#t1 == #t2)
526 for i = 1, #t1 do
527 assert(t1[i] == t2[i])
528 end
529end
530
531
524if rawget(_G, "T") then 532if rawget(_G, "T") then
525 533
526 -- memory error inside closing function 534 -- memory error inside closing function
@@ -632,6 +640,68 @@ if rawget(_G, "T") then
632 print'+' 640 print'+'
633 end 641 end
634 642
643
644 do
645 -- '__close' vs. return hooks in C functions
646 local trace = {}
647
648 local function hook (event)
649 trace[#trace + 1] = event .. " " .. (debug.getinfo(2).name or "?")
650 end
651
652 -- create tbc variables to be used by C function
653 local x = func2close(function (_,msg)
654 trace[#trace + 1] = "x"
655 end)
656
657 local y = func2close(function (_,msg)
658 trace[#trace + 1] = "y"
659 end)
660
661 debug.sethook(hook, "r")
662 local t = {T.testC([[
663 toclose 2 # x
664 pushnum 10
665 pushint 20
666 toclose 3 # y
667 return 2
668 ]], x, y)}
669 debug.sethook()
670
671 -- hooks ran before return hook from 'testC'
672 checktable(trace,
673 {"return sethook", "y", "return ?", "x", "return ?", "return testC"})
674 -- results are correct
675 checktable(t, {10, 20})
676 end
677
678end
679
680
681do -- '__close' vs. return hooks in Lua functions
682 local trace = {}
683
684 local function hook (event)
685 trace[#trace + 1] = event .. " " .. debug.getinfo(2).name
686 end
687
688 local function foo (...)
689 local x <close> = func2close(function (_,msg)
690 trace[#trace + 1] = "x"
691 end)
692
693 local y <close> = func2close(function (_,msg)
694 debug.sethook(hook, "r")
695 end)
696
697 return ...
698 end
699
700 local t = {foo(10,20,30)}
701 debug.sethook()
702 checktable(t, {10, 20, 30})
703 checktable(trace,
704 {"return sethook", "return close", "x", "return close", "return foo"})
635end 705end
636 706
637 707
@@ -640,13 +710,6 @@ print "to-be-closed variables in coroutines"
640do 710do
641 -- yielding inside closing metamethods 711 -- yielding inside closing metamethods
642 712
643 local function checktable (t1, t2)
644 assert(#t1 == #t2)
645 for i = 1, #t1 do
646 assert(t1[i] == t2[i])
647 end
648 end
649
650 local trace = {} 713 local trace = {}
651 local co = coroutine.wrap(function () 714 local co = coroutine.wrap(function ()
652 715