局部变量的更改/更新值(Lua上值)

问题描述 投票:2回答:2

我已经编写了一个脚本来热重载已经require的模块。它仅部分起作用...

我处理此任务的方法非常简单。我更改了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,但是重要/相关的片段是...

  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
2个回答
0
投票

您可以在__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
  })

这样做,您根本不需要查找上限值,因为这将迫使任何local要求结果始终引用最新资源。

[在某些情况下,这可能无法正常工作,或者以其他方式破坏了模块,但可以对其进行一些调整。


0
投票

我接受了@Nifim的回答。但是,据我所知,这仅适用于表。但是require也可以返回任何类型的值。 -不过,这是一个很好的解决方案,can可以进行一些调整...

但是,仅供参考-我的方法也能正常工作!首先,我从pcall()中删除了换行debug.getlocal(),因为这引入了另一个堆栈级别,并因此返回了不适用于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
            elseif type(value) == "table" then
                rereference(absolete, new, value)
            end
        end
    until name == nil
    thread = thread + 1
end
© www.soinside.com 2019 - 2024. All rights reserved.