Browse Source

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.
pull/25/head
Roberto Ierusalimschy 4 years ago
parent
commit
0e9254dfa0
  1. 24
      ldo.c
  2. 77
      testes/locals.lua

24
ldo.c

@ -341,7 +341,8 @@ void luaD_hookcall (lua_State *L, CallInfo *ci) {
} }
static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) { static void rethook (lua_State *L, CallInfo *ci, int nres) {
StkId firstres = L->top - nres; /* index of first result */
ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */ ptrdiff_t oldtop = savestack(L, L->top); /* hook may change top */
int delta = 0; int delta = 0;
if (isLuacode(ci)) { if (isLuacode(ci)) {
@ -360,7 +361,7 @@ static StkId rethook (lua_State *L, CallInfo *ci, StkId firstres, int nres) {
} }
if (isLua(ci = ci->previous)) if (isLua(ci = ci->previous))
L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */ L->oldpc = pcRel(ci->u.l.savedpc, ci_func(ci)->p); /* update 'oldpc' */
return restorestack(L, oldtop); L->top = restorestack(L, oldtop);
} }
@ -397,7 +398,7 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
case 1: /* one value needed */ case 1: /* one value needed */
if (nres == 0) /* no results? */ if (nres == 0) /* no results? */
setnilvalue(s2v(res)); /* adjust with nil */ setnilvalue(s2v(res)); /* adjust with nil */
else else /* at least one result */
setobjs2s(L, res, L->top - nres); /* move it to proper place */ setobjs2s(L, res, L->top - nres); /* move it to proper place */
L->top = res + 1; L->top = res + 1;
return; return;
@ -412,6 +413,8 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
wanted = codeNresults(wanted); /* correct value */ wanted = codeNresults(wanted); /* correct value */
if (wanted == LUA_MULTRET) if (wanted == LUA_MULTRET)
wanted = nres; wanted = nres;
if (L->hookmask) /* if needed, call hook after '__close's */
rethook(L, L->ci, nres);
} }
break; break;
} }
@ -426,15 +429,18 @@ static void moveresults (lua_State *L, StkId res, int nres, int wanted) {
/* /*
** Finishes a function call: calls hook if necessary, removes CallInfo, ** Finishes a function call: calls hook if necessary, moves current
** moves current number of results to proper place. ** number of results to proper place, and returns to previous call
** info. If function has to close variables, hook must be called after
** that.
*/ */
void luaD_poscall (lua_State *L, CallInfo *ci, int nres) { void luaD_poscall (lua_State *L, CallInfo *ci, int nres) {
if (L->hookmask) int wanted = ci->nresults;
L->top = rethook(L, ci, L->top - nres, nres); if (L->hookmask && !hastocloseCfunc(wanted))
L->ci = ci->previous; /* back to caller */ rethook(L, ci, nres);
/* move results to proper place */ /* move results to proper place */
moveresults(L, ci->func, nres, ci->nresults); moveresults(L, ci->func, nres, wanted);
L->ci = ci->previous; /* back to caller (after closing variables) */
} }

77
testes/locals.lua

@ -521,6 +521,14 @@ do -- tbc inside close methods
end end
local function checktable (t1, t2)
assert(#t1 == #t2)
for i = 1, #t1 do
assert(t1[i] == t2[i])
end
end
if rawget(_G, "T") then if rawget(_G, "T") then
-- memory error inside closing function -- memory error inside closing function
@ -632,21 +640,76 @@ if rawget(_G, "T") then
print'+' print'+'
end end
do
-- '__close' vs. return hooks in C functions
local trace = {}
local function hook (event)
trace[#trace + 1] = event .. " " .. (debug.getinfo(2).name or "?")
end end
-- create tbc variables to be used by C function
local x = func2close(function (_,msg)
trace[#trace + 1] = "x"
end)
print "to-be-closed variables in coroutines" local y = func2close(function (_,msg)
trace[#trace + 1] = "y"
end)
do debug.sethook(hook, "r")
-- yielding inside closing metamethods local t = {T.testC([[
toclose 2 # x
pushnum 10
pushint 20
toclose 3 # y
return 2
]], x, y)}
debug.sethook()
local function checktable (t1, t2) -- hooks ran before return hook from 'testC'
assert(#t1 == #t2) checktable(trace,
for i = 1, #t1 do {"return sethook", "y", "return ?", "x", "return ?", "return testC"})
assert(t1[i] == t2[i]) -- results are correct
checktable(t, {10, 20})
end
end
do -- '__close' vs. return hooks in Lua functions
local trace = {}
local function hook (event)
trace[#trace + 1] = event .. " " .. debug.getinfo(2).name
end
local function foo (...)
local x <close> = func2close(function (_,msg)
trace[#trace + 1] = "x"
end)
local y <close> = func2close(function (_,msg)
debug.sethook(hook, "r")
end)
return ...
end end
local t = {foo(10,20,30)}
debug.sethook()
checktable(t, {10, 20, 30})
checktable(trace,
{"return sethook", "return close", "x", "return close", "return foo"})
end end
print "to-be-closed variables in coroutines"
do
-- yielding inside closing metamethods
local trace = {} local trace = {}
local co = coroutine.wrap(function () local co = coroutine.wrap(function ()

Loading…
Cancel
Save