change/update 局部变量的值(Lua upvalue)
change/update value of a local variable (Lua upvalue)
我已经编写了一个脚本来热重载已经 require
ed 的模块。但是它只部分起作用...
我完成这项任务的方法非常简单。我更改了 Lua 的 require
函数,以便它记住与时间戳及其文件路径一起加载的模块。然后我使用 shell 脚本来观察这些文件的修改时间,并在它们发生变化时重新请求它们。我只是 dofile()
,如果没有错误发生,我将取 return 值并(重新)分配到 package.loaded[<module>]
。到目前为止一切顺利。
当我使用全局变量时,所有这些都很完美,例如foo = require "foobar"
,但是当我使用本地分配时,比如 local foo = require "foobar"
,我的热交换失败(部分)!
包似乎按预期换出,但是局部变量(来自上面的赋值)仍然保留旧引用或第一次调用 require 时获得的旧值。
我的想法是使用 Lua 的 debug.getlocal
和 debug.setlocal
函数来查找所有局部变量(堆栈中的上值)并更新它们的 values/references。
但是我得到一个错误,我想更改的上限是 "out of range"...有人可以帮我吗?我应该怎么做或如何解决这个问题?
The complete code is over at Gist,但是 important/relevant 片段是...
- 第 27 行的函数
local_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
- 和第 89 行的
debug.setlocal()
,它试图更新保存废弃模块引用的上值
-- 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
您可以在 __index
中使用元表。而不是 returning package.loaded[resource]
或 _require(resource)
return:
_require(resource)
return setmetatable({}, --create a dummy table
{
__index = function(_, k)
return package.loaded[resource][k] -- pass all index requests to the real resource.
end
})
和
package.loaded[resource] = message -- update the absolete module
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
})
这样做你根本不需要查找上值,因为这将强制任何 local
要求结果始终引用最新资源。
在某些情况下,这可能无法正常工作,或者以其他方式破坏模块,但通过一些调整它可以。
我接受了@Nifim 的回答。然而,据我所知,这只适用于表格。但是 require
也可以 return 任何类型的值。 - 尽管如此,可以进行一些调整,这是一个很好的解决方案...
但是,仅供参考 - 我的方法也有效!首先,我从 debug.getlocal()
中删除了包装 pcall()
,因为这引入了另一个堆栈级别,因此 return 编辑了不适用于 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
我已经编写了一个脚本来热重载已经 require
ed 的模块。但是它只部分起作用...
我完成这项任务的方法非常简单。我更改了 Lua 的 require
函数,以便它记住与时间戳及其文件路径一起加载的模块。然后我使用 shell 脚本来观察这些文件的修改时间,并在它们发生变化时重新请求它们。我只是 dofile()
,如果没有错误发生,我将取 return 值并(重新)分配到 package.loaded[<module>]
。到目前为止一切顺利。
当我使用全局变量时,所有这些都很完美,例如foo = require "foobar"
,但是当我使用本地分配时,比如 local foo = require "foobar"
,我的热交换失败(部分)!
包似乎按预期换出,但是局部变量(来自上面的赋值)仍然保留旧引用或第一次调用 require 时获得的旧值。
我的想法是使用 Lua 的 debug.getlocal
和 debug.setlocal
函数来查找所有局部变量(堆栈中的上值)并更新它们的 values/references。
但是我得到一个错误,我想更改的上限是 "out of range"...有人可以帮我吗?我应该怎么做或如何解决这个问题?
The complete code is over at Gist,但是 important/relevant 片段是...
- 第 27 行的函数
local_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
- 和第 89 行的
debug.setlocal()
,它试图更新保存废弃模块引用的上值
-- 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
您可以在 __index
中使用元表。而不是 returning package.loaded[resource]
或 _require(resource)
return:
_require(resource)
return setmetatable({}, --create a dummy table
{
__index = function(_, k)
return package.loaded[resource][k] -- pass all index requests to the real resource.
end
})
和
package.loaded[resource] = message -- update the absolete module
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
})
这样做你根本不需要查找上值,因为这将强制任何 local
要求结果始终引用最新资源。
在某些情况下,这可能无法正常工作,或者以其他方式破坏模块,但通过一些调整它可以。
我接受了@Nifim 的回答。然而,据我所知,这只适用于表格。但是 require
也可以 return 任何类型的值。 - 尽管如此,可以进行一些调整,这是一个很好的解决方案...
但是,仅供参考 - 我的方法也有效!首先,我从 debug.getlocal()
中删除了包装 pcall()
,因为这引入了另一个堆栈级别,因此 return 编辑了不适用于 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