diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-21 10:27:22 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2021-01-21 10:27:22 -0300 |
| commit | 0e9254dfa03d95c3aa2888cf78e9a30bc88d41bc (patch) | |
| tree | ca3c956e03f8ecc03eb7ed155c8b6f5e3ebef801 | |
| parent | 6ccd24eff58340c00db2877c4558a63c6b859442 (diff) | |
| download | lua-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.c | 24 | ||||
| -rw-r--r-- | testes/locals.lua | 77 |
2 files changed, 85 insertions, 16 deletions
| @@ -341,7 +341,8 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) { | |||
| 341 | } | 341 | } |
| 342 | 342 | ||
| 343 | 343 | ||
| 344 | static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) { | 344 | static 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 | */ |
| 432 | void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { | 437 | void 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 | |||
| 521 | end | 521 | end |
| 522 | 522 | ||
| 523 | 523 | ||
| 524 | local function checktable (t1, t2) | ||
| 525 | assert(#t1 == #t2) | ||
| 526 | for i = 1, #t1 do | ||
| 527 | assert(t1[i] == t2[i]) | ||
| 528 | end | ||
| 529 | end | ||
| 530 | |||
| 531 | |||
| 524 | if rawget(_G, "T") then | 532 | if 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 | |||
| 678 | end | ||
| 679 | |||
| 680 | |||
| 681 | do -- '__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"}) | ||
| 635 | end | 705 | end |
| 636 | 706 | ||
| 637 | 707 | ||
| @@ -640,13 +710,6 @@ print "to-be-closed variables in coroutines" | |||
| 640 | do | 710 | do |
| 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 | ||
