I saw here how to insert local variables in a table using the debug.getlocal function in Lua (5.1).
function locals()
local variables = {}
local idx = 1
while true do
local ln, lv = debug.getlocal(2, idx)
if ln ~= nil then
variables[ln] = lv
else
break
end
idx = 1 + idx
end
return variables
end
However, when I try to return the created table and access it's entries, it doesn't work.
function test1()
local v = 'I am a local!'
return locals()
end
print(test1().v) -- nil
After some trail and error, I noticed that binding the table to a variable before returning, or simply adding parentheses fixes the behavior:
function test2()
local v = 'I am a local!'
return (locals())
end
print(test2().v) -- 'I am a local!'
This is very confusing me. Why are these two programs in any way different? What am I not understanding? Does the fact that locals() is in a tail call position make any difference?
I guess what confuses you is the proper tail call feature of lua.
To understand this, we modify your locals
function, making it accept one argument as the level stack used in call to debug.getlocal
. (I'm using Lua 5.3.3)
-- Get local variables with stack level 'level'.
function locals(level)
local variables = {}
local idx = 1
while true do
local ln, lv = debug.getlocal(level, idx)
if ln ~= nil then
variables[ln] = lv
else
break
end
idx = 1 + idx
end
return variables
end
Then we modify your test functions, adding the same argument, and add a test3
function for reference.
function test1(level)
local v = 'I am a local!'
return locals(level)
end
function test2(level)
local v = 'I am a local!'
return (locals(level))
end
function test3(level)
local v = 'I am a local!'
local a = locals(level)
return a
end
Finally we add some code to run the tests.
local function printTable(t)
-- print(t)
for k, v in pairs(t) do
print(string.format("key = %s, val = %s.", k, v))
end
end
for level = 1, 3 do
print("==== Stack level: " .. tostring(level))
for num = 1, 3 do
print(string.format("What test%d returns: ", num))
printTable(_G[(string.format("test%d", num))](level))
print("")
end
end
The code above runs the test functions with different stack level and print the key-value pairs returned. My result is as follows:
==== Stack level: 1
What test1 returns:
key = variables, val = table: 0x7fa14bc081e0.
key = idx, val = 3.
key = level, val = 1.
What test2 returns:
key = variables, val = table: 0x7fa14bc08220.
key = idx, val = 3.
key = level, val = 1.
What test3 returns:
key = variables, val = table: 0x7fa14bc088b0.
key = idx, val = 3.
key = level, val = 1.
==== Stack level: 2
What test1 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 1.
key = level, val = 2.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 1.
What test2 returns:
key = level, val = 2.
key = v, val = I am a local!.
What test3 returns:
key = level, val = 2.
key = v, val = I am a local!.
==== Stack level: 3
What test1 returns:
key = (*temporary), val = function: 0x109f5a070.
What test2 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 2.
key = level, val = 3.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 2.
What test3 returns:
key = (for step), val = 1.
key = (for limit), val = 3.
key = (for index), val = 3.
key = level, val = 3.
key = printTable, val = function: 0x7fa14bc08360.
key = (*temporary), val = function: 0x7fa14bc08360.
key = num, val = 3.
When level
is 1, locals
works well to give its own local variables. But when level
is 2, test1
returns variables of the outer scope, whereas test2
and test3
give the result you expect. For stack level 3 test2
and test3
return something like test1
at stack level 2. So it seems test1
skips a stack level, and the only explanation I could think of is the proper tail call.
According to PIL (the link I provide at the beginning), a proper tail call will never cause the stack to overflow, which I take as doing the call in some inline way. If I'm right with that, this explains the skipping behaviour of test1
's return statement, because that's a proper tail call, and the only one in the 3 test functions.