debug.getinfo(..).name is set/nil depending on subtle differences

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

debug.getinfo(..).name is set/nil depending on subtle differences

Federico Ferri
I found an issue with my code and debug.getinfo() which I reduced to the following testcase:

function g()
    for k,v in pairs(debug.getinfo(1,'n')) do print(k..'='..v) end
end

function f1()
    local r=g()
    return r
end

function f2()
    return g()
end

f1() -- prints namewhat=global name=g
f2() -- prints namewhat=

So, why in f2 the field 'name' is nil?

Cheers,
Federico Ferri
Reply | Threaded
Open this post in threaded view
|

Re: debug.getinfo(..).name is set/nil depending on subtle differences

Francisco Olarte
Federico:

On Wed, Dec 16, 2020 at 12:00 PM Federico Ferri
<[hidden email]> wrote:
> I found an issue with my code and debug.getinfo() which I reduced to the following testcase:
....
> So, why in f2 the field 'name' is nil?

Mandatory tail call elimination? I get this, have not dug more as I'm
not too familiar with the expected returns:

Lua 5.3.3  Copyright (C) 1994-2016 Lua.org, PUC-Rio

> function g()
>>     for k,v in pairs(debug.getinfo(1,'n')) do print(k..'='..v) end
>> end
>
> function f1()
>>     local r=g()
>>     return r
>> end
>
> function f2()
>>     return g()
>> end
> f1()
name=g
namewhat=global
nil
> f2()
namewhat=
> g()
namewhat=

Francisco Olarte.
Reply | Threaded
Open this post in threaded view
|

Re: debug.getinfo(..).name is set/nil depending on subtle differences

Andrew Gierth
>>>>> "Francisco" == Francisco Olarte <[hidden email]> writes:

 >> So, why in f2 the field 'name' is nil?

 Francisco> Mandatory tail call elimination? I get this, have not dug
 Francisco> more as I'm not too familiar with the expected returns:

getfuncname (at least in 5.4.2) includes this:

  /* calling function is a known Lua function? */
  else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous))
    return funcnamefromcode(L, ci->previous, name);

Lua function values don't have any "name" property inherent to the
function; that is, the only "name" a function has is the name used to
refer to some location from which the function value was fetched (so a
function might have many names or none).

What getinfo does is try and look at the bytecode instructions of the
calling function to determine where it got the function value from. But
this is not possible if the caller was not Lua code, or if the call was
a tail call (since in the latter case there's no information about the
caller).

Notably, what luaL_traceback does is somewhat different (and likely much
slower): it first looks to see if the function value is found under a
string key in any table stored in package.loaded (actually
registry._LOADED but they are initially the same table), and only if it
is not does it use the name from debug.getinfo.

--
Andrew.
Reply | Threaded
Open this post in threaded view
|

Re: debug.getinfo(..).name is set/nil depending on subtle differences

Tom Sutcliffe


> On 16 Dec 2020, at 11:52 am, Andrew Gierth <[hidden email]> wrote:
>
> Lua function values don't have any "name" property inherent to the
> function; that is, the only "name" a function has is the name used to
> refer to some location from which the function value was fetched (so a
> function might have many names or none).

I must admit I've long wanted `function foo()` to store some sort of "declared name" property such that debug.getinfo would always return "foo", without all the magic funkyness of examining calling bytecode or module tables. I get that `function foo()` is merely a shorthand for `foo = function()`, and that logically there's no single name for a first-class function, but I'd be very much in favour of weakening that equivalence a little just to make debugging easier. While of course having the two syntaxes continue to otherwise behave identically at runtime.

I've ended up more than once in a situation with some dynamic dispatch logic that basically guaranteed the dynamically-dispatched function would never be named in stacktraces - to the point where I had a dynamically-generated helper function in debug builds just so that stacktraces would look friendlier. I don't have it to hand but it made the call through a named stack variable so the calling bytecode trick would find a name, and it was created with load() of a small string snippet. It would be nice not to have to go to such lengths!

Cheers,

Tom