3

requireすでに編集されているモジュールをホットリロードするスクリプトを作成しました。部分的にしか機能しませんが...

このタスクに対する私のアプローチは非常に単純です。Lua のrequire関数を変更して、ロードしたモジュールをタイムスタンプとそのファイル パスと共に記憶するようにしました。次に、シェル スクリプトを使用してこれらのファイルの変更時刻を監視し、変更された場合はそれらを再要求します。dofile()エラーが発生しない場合は、戻り値を取得して (再) 割り当てますpackage.loaded[<module>]。ここまでは順調ですね。

などのグローバル変数を使用すると、これらすべてが完全に機能しますfoo = require "foobar"が、 のようなローカル割り当てを使用するlocal foo = require "foobar"と、ホットスワッパーが (部分的に) 失敗します。

パッケージは意図したとおりにスワップ アウトされているようですが、ローカル変数 (上記の割り当てによる) は古い参照または require が初めて呼び出されたときに取得した古い値を保持しています。

私のアイデアは、Luadebug.getlocaldebug.setlocal関数を使用してすべてのローカル変数 (スタック内の上位値) を見つけ、それらの値/参照を更新することでした。

しかし、変更したい上限値が「範囲外」であるというエラーが表示されます...誰か助けてくれませんか? どうすればよいですか、またはこれを回避するにはどうすればよいですか?

完全なコードは Gistにありますが、重要/関連するスニペットは...

  1. local_upvalues()利用可能なすべての上位値を収集する 27 行目の関数
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()廃止されたモジュール参照を保持する 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
4

2 に答える 2

0

@Nifimの回答を受け入れました。ただし、私が知る限り、それはテーブルに対してのみ機能します。ただし、require任意の型の値を返すこともできます。- それにもかかわらず、それはいくつかの微調整で機能する素晴らしいソリューションです...

ただし、参考までに、私のアプローチも機能するようになりました。最初に、ラッピングを削除しましpcall()debug.getlocal()。これにより、別のスタック レベルが導入され、誤ったスレッドとインデックスの値が返されたため、 では機能しませんでしたdebug.setlocal()。最後に、debug.setlocal呼び出しを同じ関数 (= 同じスコープ) に移動して、チェックと再割り当てを 1 ステップで行うようにしました。

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
于 2019-12-27T21:09:32.357 に答える