如何从 Lua 脚本中获取有关函数调用的信息?
How do I get information about function calls from a Lua script?
我有一个用 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 并找到我感兴趣的函数调用。
还有其他建议吗?
如果你不能mod化文件,你可以将文件读入字符串然后解析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
结果table然后可以序列化
['foo'] = {{"'a'", " 1"}, {"'b'"}}
3 个可能的陷阱:
M
不是一个非常独特的名称,可能会有所不同,可能匹配对另一个库的意外函数调用。
- 如果在 arg 列表中进行了函数调用,则此示例不处理。例如
myfunc(getStuff(), true)
- 生成的 table 不知道 args 的类型,所以它们都保存为字符串表示形式。
如果可以选择 mod 化目标文件,您可以围绕 required
module
创建一个包装器
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')
如果您的 module 不仅仅是 function
s,您需要在包装器中处理它,该包装器假定所有索引都是一个函数。
在所有调用之后,您可以序列化 calls
table 以获取所有调用的历史记录。对于示例代码,table 看起来像
{
['foo'] = {{'a', 1}, {'b'}}
}
我有一个用 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 并找到我感兴趣的函数调用。
还有其他建议吗?
如果你不能mod化文件,你可以将文件读入字符串然后解析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
结果table然后可以序列化
['foo'] = {{"'a'", " 1"}, {"'b'"}}
3 个可能的陷阱:
M
不是一个非常独特的名称,可能会有所不同,可能匹配对另一个库的意外函数调用。- 如果在 arg 列表中进行了函数调用,则此示例不处理。例如
myfunc(getStuff(), true)
- 生成的 table 不知道 args 的类型,所以它们都保存为字符串表示形式。
如果可以选择 mod 化目标文件,您可以围绕 required
module
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')
如果您的 module 不仅仅是 function
s,您需要在包装器中处理它,该包装器假定所有索引都是一个函数。
在所有调用之后,您可以序列化 calls
table 以获取所有调用的历史记录。对于示例代码,table 看起来像
{
['foo'] = {{'a', 1}, {'b'}}
}