lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Dear List,

On Wed, Jan 10, 2018 at 08:08:58AM +0200, Dirk Laurie wrote:
> 2018-01-09 22:09 GMT+02:00 dyngeccetor8 <dyngeccetor8@disroot.org>:
> 
> >> I wrote a simple file "essai.lua" containing :
> >>
> >> --
> >> function essai1()
> >>   print(debug.getinfo(1,"n").name)
> >> end
> >>
> >> essai1()
> >> --
> >>
> >> When I do
> >>
> >> $ lua essai.lua
> >> essai1
> >>
> >> This is logical to me.
> >>
> >> Now, if I start lua and *copy and paste* the same code as in "essai.lua", I have
> >>
> >> $ lua
> >> Lua 5.3.4  Copyright (C) 1994-2017 Lua.org, PUC-Rio
> >>> function essai1()
> >>>>   print(debug.getinfo(1,"n").name)
> >>>> end
> >>>
> >>> essai1()
> >> nil
> >>
> >> [...]
> >>
> >> Philippe
> >
> > Wrap in do-end block. (I dont know why it works either.)
> >
> >   Lua 5.3.4  Copyright (C) 1994-2017 Lua.org, PUC-Rio
> >   > function essai1()
> >   >>   print(debug.getinfo(1,"n").name)
> >   >> end
> >   >
> >   > essai1()
> >   nil
> >   > do
> >   >> function essai1()
> >   >>   print(debug.getinfo(1,"n").name)
> >   >> end
> >   >>
> >   >> essai1()
> >   >> end
> >   essai1
> 
> This is a very interesting question, especially to someone that
> has contributed a Rock [1] which heavily relies on debug.getinfo.
> 
> debug.getinfo(1,"n").name is an attempt to find a local name
> for the running function. It does not as a general rule look
> for global names. So the mystery is not why you failed to get
> the name when you defined the function in interactive mode,
> it is why you did get it when running it from a file.
> 
> The only explanation that I can think of is based on chunks.
> 
> A chunk is a portion of code that is offered as a whole to
> the Lua interpreter to be translated to bytecode. For example:
>   1. A line that you type in interactive mode (or several lines,
> if it is waiting for "end","]]" etc) is one chunk.
>   2. A file that you execute with "dofile" or load with "loadfile"
> is one chunk.
>   3. The string that you pass to "load" is one chunk.
> 
> Now if you do what you did the first time or what Martin did,
> the definition of the function and its execution happen in
> the same chunk. The code would still have worked if you
> defined 'essai1" as a local function. But in interactive mode,
> if you define "essa1" on one line and call it on another, that's
> two chunks and if "essa1" was local, you can't see it any more.
> 
> So what we observe is that global names defined in the same
> chunk are taken into account by debug.getinfo (you might
> say they have honorary local status) but global names defined
> elsewhere are not.
> 
> Where does that status come from? I think it may have something
> to do with _ENV. Whenever you compile a chunk, you get a new
> upvalue named _ENV. The outside _ENV is not visible. (Its
> contents is, unless you yourself change it, because it starts
> off as equal to the old one.)
> 
> At this stage it is only a guess. One will have to make a bytecode
> listing (with "luac -ll") and maybe even read the Lua source to
> make sure.
> 
> [1] luarocks install ihelp
> 

To investigate this further, I added some printf's in getfuncname() in
ldebug.c:

  static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
    if (ci == NULL) {  /* no 'ci'? */
      printf( "getfuncname: No info\n" );
      return NULL;  /* no info */
    }
    else if (ci->callstatus & CIST_FIN) {  /* is this a finalizer? */
      printf( "getfuncname: Finalizer\n" );
      *name = "__gc";
      return "metamethod";  /* report it as such */
    }
    /* calling function is a known Lua function? */
    else if (!(ci->callstatus & CIST_TAIL) && isLua(ci->previous)) {
      printf( "getfuncname: !(ci->callstatus & CIST_TAIL) = %d,  isLua(ci->previous) = %d: return funcnamefromcode()\n", !(ci->callstatus & CIST_TAIL), isLua(ci->previous) );
      return funcnamefromcode(L, ci->previous, name);
    }
    else {
      printf( "getfuncname: !(ci->callstatus & CIST_TAIL) = %d,  isLua(ci->previous) = %d: return NULL\n", !(ci->callstatus & CIST_TAIL), isLua(ci->previous) );
      return NULL;  /* no way to find a name */
    }
  }

This function gets called when using debug.getinfo() to attempt to find
the name of a function.

With these modifications:

  $ lua
  Lua 5.3.4  Copyright (C) 1994-2017 Lua.org, PUC-Rio
  > function essai1()
  >>   print(debug.getinfo(1,"n").name)
  >> end
  > essai1()
  getfuncname: !(ci->callstatus & CIST_TAIL) = 0,  isLua(ci->previous) = 0: return NULL
  nil

Here we see the original scenario repeated, with the additional
information that, in fact, both the conditions

  !(ci->callstatus & CIST_TAIL)

and

  isLua(ci->previous)

are 0, that is, false.

Considering the second of these conditions first and interpreting it as
"called from Lua code", it seems reasonable to assume that it is false
because the caller is, in fact, the Lua interpreter.

To help understand the first condition, lstate.h defines:

  #define CIST_TAIL  (1<<5)  /* call was tail called */

So the first condition I interpret as "function is not called in a tail
call". As explained in the Lua reference manual
(https://www.lua.org/manual/5.3/manual.html#3.4.10):

  in a tail call, the called function reuses the stack entry of the
  calling function.

Thus, if in a tail call, there is no Lua-style stack entry available for
the calling function. (See also "Programming in Lua", 4th edition, "6.4
Proper Tail Calls", page 56.)

The possibility of stack entry reuse means that the second condition
must be re-interpreted as "previous stack entry is a Lua-style stack
entry" instead of "called from Lua code".

It can be seen that both conditions, not in a tail call and previous
stack entry is a Lua-style stack entry, are important to ensure that a
proper Lua-style stack entry is available for the calling function. And
in this case, getfuncname() calls funcnamefromcode() which proceeds to
inspect the available stack entry and code of the caller in an attempt
to find a suitable name for the called function. Note that even with a
Lua-style stack entry of the calling function available, it is still not
certain that a reasonable name for the called function can be found.

Continuing the Lua interpreter session:

  > function f() essai1() end
  > f()
  getfuncname: !(ci->callstatus & CIST_TAIL) = 1,  isLua(ci->previous) = 2: return funcnamefromcode()
  essai1

This is similar to the suggestion by dyngeccetor8 and shows that if the
call is performed from Lua code and is not a tail call, the name can be
found.

The reference manual explains that

  A call of the form return functioncall is called a tail call.

So let us try that:

  > function g() return essai1() end
  > g()
  getfuncname: !(ci->callstatus & CIST_TAIL) = 0,  isLua(ci->previous) = 0: return NULL
  nil

And, indeed, now getfuncname() detects that essai1() is in a tail call
and no name can be found. But we also see that the previous stack entry
is no longer a Lua-style stack entry, so let us finally try:

  > function h() g() end
  > h()
  getfuncname: !(ci->callstatus & CIST_TAIL) = 0,  isLua(ci->previous) = 2: return NULL
  nil

This time, the previous stack entry is a Lua-style stack entry, namely
that of h(). Nevertheless, because the call from g() of essai1() is
still a tail call, the name can again not be found.

Thanks a lot for an enlightening and interesting question.

Best regards
Thorkil Naur