【问题标题】:change/update value of a local variable (Lua upvalue)更改/更新局部变量的值(Lua upvalue)
【发布时间】:2020-04-16 23:07:12
【问题描述】:

我已经编写了一个脚本来热重载requireed 模块。但是它只能部分工作......

我完成这项任务的方法非常简单。我更改了 Lua 的 require 函数,以便它记住与时间戳及其文件路径一起加载的模块。然后我使用一个 shell 脚本来观察这些文件的修改时间,并在它们发生变化时重新要求它们。我只是dofile(),如果没有发生错误,我将返回值并(重新)分配给package.loaded[<module>]。到目前为止一切顺利。

当我使用全局变量时,所有这些都很完美,例如foo = require "foobar",但是当我使用本地分配时,比如local foo = require "foobar",我的热交换失败(部分)!

似乎包被按预期换出,但是局部变量(来自上面的赋值)仍然保存旧引用或它在第一次调用 require 时获得的旧值。

我的想法是使用 Lua 的 debug.getlocaldebug.setlocal 函数来查找所有局部变量(堆栈中的上值)并更新它们的值/引用。

但是我得到一个错误,我想要更改的上值是“超出范围”......有人可以帮我吗?我应该怎么做或如何解决这个问题?

The complete code is over at Gist,但是重要/相关的 sn-ps 是...

  1. 第 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
  1. 和第 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

【问题讨论】:

    标签: lua hot-reload upvalue


    【解决方案1】:

    您可以使用带有__index 的元表。而不是返回 package.loaded[resource]_require(resource) 返回:

    _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
      })
    

    这样做您根本不需要查找 upvalue,因为这将强制任何 local 要求结果始终引用最新资源。

    在某些情况下,这可能无法正常工作,或者会破坏模块,但通过一些调整可以。

    【讨论】:

    • 我必须尝试...毕竟这是一个不错的选择。我看到这个失败的唯一情况是我使用的类模块(通过元表继承),它也使用 __index 键。但我会研究这个,因为它比重新引用所有本地和全局变量更便宜。谢谢。
    【解决方案2】:

    我接受了@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
    

    【讨论】:

      猜你喜欢
      • 2016-09-28
      • 2014-01-31
      • 1970-01-01
      • 2013-07-08
      • 2015-12-11
      • 1970-01-01
      • 2013-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多