如何理解“只有在 Lua 执行 Lua 函数时,当它跳回到代码中时,lua_Hook 才被调用(该事件仅发生一次)”?

根据文档(https://www.lua.org/manual/5.3/manual.html#lua_sethook),该文档称[empasise mine]:

参数 f 是钩子函数。mask 指定在哪些事件上调用钩子:它由常数的按位或组成 LUA_MASKCALL,LUA_MASKRET,LUA_MASKLINE 和 LUA_MASKCOUNT。计数 参数仅在掩码包括 LUA_MASKCOUNT 时才有意义。对于 每个事件,将根据下面所述调用钩子:

调用钩子:当解释器调用函数时调用它。 钩子在 Lua 进入新函数之后立即被调用,在 函数获得其参数之前。

返回钩子:在从函数返回时调用 解释器。在 Lua 离开函数之前,会在钩子之前调用。没有标准方式来访问函数返回的值。

行钩子:在 解释器即将开始执行新的一行代码时调用,或者 它跳回代码中(即使是到同一行)。 (此事件 仅在 Lua 执行 Lua 函数时发生。)

如何理解“只有在 Lua 执行 Lua 函数时,当它跳回到代码中时,lua_Hook 才被调用(该事件仅发生一次)”?

点赞
用户1442917
用户1442917

我们可以查看源代码(Lua 5.4):

int luaG_traceexec (lua_State *L, const Instruction *pc) {
  -- some parts removed
  if (mask & LUA_MASKLINE) {
    if (npci == 0 ||  /* 当进入新函数、*/
        pc <= L->oldpc ||  /* 当回到之前的位置(循环)或者*/
        changedline(p, pcRel(L->oldpc, p), npci)) {  /* 进入新的行数 */
      int newline = luaG_getfuncline(p, npci);
      luaD_hook(L, LUA_HOOKLINE, newline, 0, 0);  /* 调用行 hook */
    }
  }
  return 1;  /* 保持“陷阱” */
}

可以看到,LUA_HOOKLINE 在三种情况下被调用:(1) 进入新的函数,(2) 回到之前的位置(循环),或者 (3) 进入新的行数。

“进入新行数”是通过调用 changedline 进行检查的:

static int changedline (const Proto *p, int oldpc, int newpc) {
  while (oldpc++ < newpc) {
    if (p->lineinfo[oldpc] != 0)
      return (luaG_getfuncline(p, oldpc - 1) != luaG_getfuncline(p, newpc));
  }
  return 0;  /* 路上没有行数改变 */
}

如果新指令与上一条指令在不同行,则返回 true。

luaG_getfuncline 获取指令的行数(如果有可用的调试信息)。被检查的函数是基于当前调用信息值的活动 lua 函数。

2020-12-22 05:50:40