改变/更新本地变量的值(Lua upvalue)
我编写了一个脚本来热重载已经require的模块。但是只能部分工作...
我对这个任务的方法很简单。我改变了Lua的require函数,这样它就会记住已加载的模块及其时间戳和文件路径。然后我使用一个shell脚本来观察这些文件的修改时间,如果它们发生了变化,我就重新require它们。我只需使用dofile(),如果没有错误发生,我就取回返回值并将它(重新)分配给package.loaded[
当我使用全局变量,例如foo = require"foobar"时,所有这些都完美地运作,但是当我使用本地分配时,例如local foo = require "foobar"时,我的热交换(部分)失败了!
似乎包被像预期的那样替换了,但是局部变量(来自上面的分配)仍然保留着旧引用或第一次调用require时得到的旧值。
我的想法是使用Lua的debug.getlocal和debug.setlocal函数来查找所有本地变量(堆栈中的upvalue)并更新它们的值/引用。
但是我得到了一个错误,即我想要更改的upvalue是"out of range"...请问有人可以帮帮我吗?我该怎么做或如何解决这个问题?
完整的代码在Gist上,但重要/相关的片段是...
1.第27行的函数local_upvalues(),它收集所有可用的upvalues
local function local_upvalues()
local upvalues = {}
local failures = 0
local thread = 0
while true do
thread = thread + 1
local index = 0
while true do
index = index + 1
local success, name, value = pcall(debug.getlocal, thread, index)
if success and name ~= nil then
table.insert(upvalues, {
name = name,
value = value,
thread = thread,
index = index
})
else
if index == 1 then failures = failures + 1 end
break
end
end
if failures > 1 then break end
end
return upvalues
end
2.第89行的debug.setlocal(),它尝试更新保存过时模块引用的upvalue
-- update module references of local upvalues
for count, upvalue in ipairs(local_upvalues()) do
if upvalue.value == package.loaded[resource] then
-- print(upvalue.name, "updated from", upvalue.value, "to", message)
table.foreach(debug.getinfo(1), print)
print(upvalue.name, upvalue.thread, upvalue.index)
debug.setlocal(upvalue.thread, upvalue.index, message)
end
end
package.loaded[resource] = message -- update the absolete module
我接受了 @Nifim 的答案。但是据我所知,那只适用于表格。但是 require 也可以返回任何类型的值。 - 然而,这是一个很好的解决方案,可以通过一些调整来发挥作用...
然而,仅供参考 - 我的方法也可以工作!首先,我从 debug.getlocal() 中删除了包裹在 pcall() 中的部分,因为这会导致另一个堆栈级别,并且返回错误的线程和索引值,这些值与 debug.setlocal() 不兼容。最后,我将 debug.setlocal 调用移动到同一个函数中(=同一作用域),这样我可以在一步中检查并重新分配!
请参见下面的 rereference(absolete, new) 函数代码。
local thread = 1
while debug.getinfo(thread) ~= nil do
local index, name, value = 0, nil, nil
repeat
index = index + 1
name, value = debug.getlocal(thread, index)
if name ~= nil
and name ~= "absolete"
and name ~= "new"
then
if value == absolete then
if debug.setlocal(thread, index, new) == name then
print(string.format(
"%s local upvalue '%s' has been re-referenced",
os.date("%d.%m.%Y %H:%M:%S"),
name
))
end
end
end
until name == nil
thread = thread + 1
end
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?

你可以使用带有
__index的元表。而不是返回package.loaded[resource]或_require(resource), 返回:_require(resource) return setmetatable({}, --创建一个虚拟表 { __index = function(_, k) return package.loaded[resource][k] --将所有索引请求传递给真正的资源。 end })并且
package.loaded[resource] = message --更新过时的模块 print(string.format("%s %s hot-swap of module '%s'", os.date("%d.%m.%Y %H:%M:%S"), stateless and "stateless" or "stateful", hotswap.registry[resource].url )) return setmetatable({}, { __index = function(_, k) return package.loaded[resource][k] end })这样做,您就不需要查找所有的upvalues,因为这会强制所有的局部要求结果总是引用最新的资源。
可能有情况下,这样将不起作用或破坏模块,但通过一些调整,可以实现。