lua-users home
lua-l archive

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

在 2015/5/19 16:33, Philipp Janda 写道:

In the meantime I have come across another case of inconsistent behavior while implementing my line hook workaround (this one I can definitely fix with an explicit return -- or any extra statement for that matter):

If I have a chunk that consists entirely of local function definitions (using the syntax sugar) I can't get the last local variable from within the line hook (this can actually be explained via the manual since the line hook is called *before* a line is evaluated). However, if I don't use the syntax sugar but `local x; function x() end` instead, then I *can* get that last local. Also if I use normal functions instead of chunks, I can get the last local no matter what syntax I use. This applies to Lua 5.2, 5.3, and LuaJIT (Lua 5.1 is more consistent and I never get the last local).

it is nothing to do with whether you use the "chunk loading" way or "function definition" way.
try this (note the subtle difference from your func1 & func3. explanation follows):

  local func5 = (loadstring or load)[[
    local function x()
    local function y()
  local func6 = function ()
    local function x()
    local function y()
    end end

detailed explanation:

it is due to the way how the VM decides whether to call a line hook: the VM execute
a Lua function instruction(i.e. VM OPCODE) by instruction, not line by line.
it uses the debug info to "guess" which line is the "current line". before executing
an instruction, the VM checks if the line number value of the to-be-executed instruction
is different from the line number of the last-executed instruction. if so,the line hook
should be called.

in your test script, func1 and func3 actually have identical instruction sequences,
but the line number info are different. by using `string.dump` function, and
the `luac -l` command, you can see the listings of the functions:

=============== listing of func1 =================
main <(string):0,0> (3 instructions at 0000000000818060)
0+ params, 2 slots, 1 upvalue, 2 locals, 0 constants, 2 functions
    1    [2]    CLOSURE      0 0    ; 00000000008180F0
    2    [4]    CLOSURE      1 1    ; 0000000000818210
    3    [4]    RETURN       0 1
constants (0) for 0000000000818060:
locals (2) for 0000000000818060:
    0    x    2    4
    1    y    3    4
upvalues (1) for 0000000000818060:
    0    _ENV    1    0

================ listing of func3 =================
function <lhook.lua:15,20> (3 instructions at 0000000000658060)
0 params, 2 slots, 0 upvalues, 2 locals, 0 constants, 2 functions
    1    [17]    CLOSURE      0 0    ; 00000000006580F0
    2    [19]    CLOSURE      1 1    ; 0000000000658180
    3    [20]    RETURN       0 1
constants (0) for 0000000000658060:
locals (2) for 0000000000658060:
    0    x    2    4
    1    y    3    4
upvalues (0) for 0000000000658060:

see the line number difference? note the RETURN instruction at the end.
(the _ENV upvalue is irrelevant with this line hook subject)

how come that difference?

Lua always generate an RETURN instruction no matter whether there is an
explicit return statement. the parser emits this instruction at the end
of a function.

for func1, it is the EOS token that finish the function definition.
when the parser sees the EOS token, it emit the RETURN instruction and
put the line number to the debug info. the lexer does not update the line
number for the EOS token, thus the RETURN instruction has the same line number
of the previous CLOSURE instruction.

for func3, it is the `end` keyword that finish the function definition.
this time the lexer has the "correct" line number info for that token.

now I believe you know the difference between func1, func3 (and the above func5 & func6)

for func2 and func4, you can check the listings yourself.
they use 1 more stack slot and 2 more instructions than func1 & func3.
hint: you don't really _declare_ (uninitialized, like C) local variables in Lua.

p.s. in the hook function, you may print the function name and line number before
enumerating local variables, so that you can get a better impression about when and how
the hook is called.



the nerdy Peng / 书呆彭 / Sent from Thunderbird