每个人都知道,Lua中的变量(如果未明确定义为“局部”)将是全局变量。有时这会引起问题,例如覆盖库函数,或者意外地为另一个具有相同名称的全局变量提供值。因此,如果有一种方法可以找到在单个Lua代码文件中定义的所有全局变量,那将非常有帮助。
但是,我没有找到有关这个看似受欢迎的问题的任何线索。我可以上网获得的最佳答案是使用_G在环境中打印所有全局变量,这并没有太大帮助。我目前正在使用Emmylua在Intellij Idea中对Lua进行编码,Emmylua是一种功能强大的工具,可以以特殊的样式显示全局变量,并且可以轻松地将全局变量追溯到其定义。但是当代码变得很长时,这也无济于事。
所以基本上,我只想获取在给定的Lua代码文件中定义的全局变量的列表。使用工具或出色的功能。如果可以简化事情,我们可以假定代码文件是一个模块。如果它可以进一步打印这些全局变量的定义位置,那就更好了。有人可以帮我吗?
答案 0 :(得分:0)
卢阿(Lua)无法判断何时引入全局变量。
特别是值是一个函数,debug.getinfo
可以通过告诉您定义函数的位置(通常但不总是将函数全局化的位置)来提供帮助。 / p>
您可以在引入全局信息时捕获所需的信息。这可以通过在全局表上使用__newindex
方法设置一个元表来完成。引入新的全局变量时(而不是覆盖现有全局变量时),将调用此方法。在这种方法中,您可以使用debug.getinfo
找出呼叫者的来源。还要注意,如果您的任何其他代码都试图在全局环境中使用元表,则必须对其进行良好的操作。 (它只能有一个元表。)
您还可以避免使用全局表。这样做的一种中间方法是覆盖环境。在Lua 5.2和Lua 5.3中,这是通过声明一个名为_ENV
的本地表来完成的-对全局表的所有访问都将访问该表。 (实际上,全局访问默认始终使用_ENV
,默认情况下_ENV
是_G
。)通过为该_ENV
提供一个将访问权限转发到{{ 1}}(或任何其他环境)。此处的区别在于,即使_G
中存在绑定,__newindex
仍将被调用,因此此方法可以检测替代。
使用_G
,尽管本质上是作用域的本地变量(例如,每个文件都需要覆盖它)。这样的钩子也可以全局安装。如果使用_ENV
函数手动加载模块(不太可能),则可以仅提供自定义load
作为参数。如果您使用_ENV
,则可以通过覆盖require
中的Lua搜索程序来获得执行之前已保存文件的保留。这是package.searchers[2]
调用的内置函数,用于在文件系统中查找文件并加载。返回值是require
然后运行的已加载函数。因此,在加载之后但返回到require
之前,您可以使用require
覆盖默认的debug.setupvalue
值(如果有)。
示例代码(仅经过轻微测试):
_ENV
请注意,此处的钩子仅适用于运行此代码后所需的文件,并且基本上仅适用于通过内置local global_info = {}
local default_searcher2 = package.searchers[2]
package.searchers[2] = function(...)
local result = default_searcher2(...)
local parent_environment = _G
local my_env = setmetatable({}, {
__index = parent_environment,
__newindex = function(self, k, v)
local new_info = debug.getinfo(2)
-- keeping rich data like this could be a memory leak
-- if some globals are assigned repeatedly, but that
-- may still be okay in a debugging scenario
local history = global_info[k]
if history == nil then
history = {}
global_info[k] = history
end
table.insert(history, {info = new_info, value = v})
parent_environment[k] = v
end,
})
if type(result) == "function" then
debug.setupvalue(result, 1, my_env)
end
return result
end
function gethistory(name)
local history = global_info[name]
if history == nil then
print('"' .. name .. '" has never been defined...')
else
print('History for "' .. name .. '":')
for _, record in ipairs(history) do
print(record.info.short_src .. ": " .. record.info.currentline)
end
end
end
包含的Lua文件(不适用于C库)。它不会在全局环境中设置一个元表,因此不会在全局环境中发生冲突,但是如果文件直接访问require
(或例如在目录中访问_G
而不是_G
,则可以绕开它他们自己的_ENV
表)。也可以考虑此类问题,但这可能是一个兔子洞,具体取决于您需要此补丁的“不可见”程度。
在Lua 5.1中,您拥有_ENV
而不是_ENV
,我相信它可以起到类似的作用。
还要注意,我概述的所有方法只能检测在运行时实际执行的全局访问。
答案 1 :(得分:0)
是的。本地与全局是一个具有约束力的问题,它主要在编译时确定。当然,设置变量是在编译时确定的。
Lua提供了luac
编译器,该编译器将参数-l
用于列表。
在Lua 5.1中,有一个操作码SETGLOBAL
。列指示语句的行号,注释指示全局名称。
在5.2及更高版本中,存在操作码SETTABUP
。列指示语句的行号,注释指示表和键的名称。 _ENV
高值引用的表中有“全局变量”。
因此,您可以使用Lua提供的工具轻松找到设置全局变量的任何语句的行号。
BTW-在许多模块系统中,模块脚本不会设置任何全局变量。