【问题标题】:How do I get information about function calls from a Lua script?如何从 Lua 脚本中获取有关函数调用的信息?
【发布时间】:2021-03-18 10:19:40
【问题描述】:

我有一个用 Lua 5.1 编写的脚本,它导入第三方模块并从中调用一些函数。我想从带有参数的模块中获取函数调用列表(当它们在执行之前已知时)。

因此,我需要编写另一个脚本,它获取我的第一个脚本的源代码,对其进行解析,并从其代码中提取信息。

考虑最小的例子。

我有以下模块:

local mod = {}

function mod.foo(a, ...)
    print(a, ...)
end

return mod

还有如下驱动代码:

local M = require "mod"
M.foo('a', 1)
M.foo('b')

使用M.foo 函数的“使用”事件来检索数据的更好方法是什么?

理想情况下,我想获取包含被调用函数名称及其参数值的信息。从上面的示例代码中,得到这样的映射就足够了:{'foo': [('a', 1), ('b')]}

我不确定 Lua 是否有反射函数来检索这些信息。所以我可能需要使用现有的 Lua 解析器之一来获取完整的 AST 并找到我感兴趣的函数调用。

还有其他建议吗?

【问题讨论】:

标签: reflection lua abstract-syntax-tree


【解决方案1】:

如果您无法修改文件,您可以将文件读入字符串,然后解析mod 文件并找到其中的所有函数,然后使用该信息解析目标文件以供 mod 库的所有用途

functions = {}

for func in modFile:gmatch("function mod%.(%w+)") do
    functions[func] = {}
end

for func, call in targetFile:gmatch("M%.(%w+)%(([^%)]+)%)") do
    args = {}
    for arg in string.gmatch(call, "([^,]+)") do
        table.insert(args, arg)
    end

    table.insert(functions[func], args)
end

然后可以对结果表进行序列化

    ['foo'] = {{"'a'", " 1"}, {"'b'"}}

3 个可能的陷阱:

  1. M 不是一个非常独特的名称,并且可能会与对另一个库的意外函数调用匹配。
  2. 如果在 arg 列表中进行了函数调用,则此示例不处理。例如myfunc(getStuff(), true)
  3. 结果表不知道 args 的类型,因此它们都保存为字符串表示形式。

如果修改目标文件是一个选项,您可以在 required 模块周围创建一个包装器

function log(mod)
    local calls = {}
    local wrapper = {
        __index = function(_, k)
            if mod[k] then
                return function(...)
                    calls[k] = calls[k] or {}
                    table.insert(calls[k], {...})

                    return mod[k](...)
                end
            end
        end,
    }

    return setmetatable({},wrapper), calls
end

然后你像这样使用这个函数。

local M, calls = log(require("mod"))
M.foo('a', 1)
M.foo('b')

如果您的模块不仅仅是functions,您需要在包装器中处理它,这个包装器假定所有索引都是一个函数。

在所有调用之后,您可以序列化calls 表以获取所有调用的历史记录。对于示例代码,表格如下所示

{
    ['foo'] = {{'a', 1}, {'b'}}
}

【讨论】:

  • 谢谢,这是一个非常好的解决方案。但是,理想情况下,我想在不修改脚本的情况下获得相同的数据。我知道使用 require 周围的包装器无法做到这一点,所以我现在将问题留待解决,也许有人会有其他想法。
  • 我添加了一个通过读取目标文件来工作的解决方案。
  • 嗯,使用正则表达式是避免使用完整 Lua 解析器的另一种选择。谢谢,我明白了。我想也许该语言中有一些特殊的库或反射工具可以简化这项任务,但显然,我将不得不解析代码。
猜你喜欢
  • 2010-11-15
  • 2018-04-23
  • 1970-01-01
  • 2016-09-15
  • 2019-06-02
  • 1970-01-01
  • 2011-07-22
相关资源
最近更新 更多