Песочница Lua функции - PullRequest
       2

Песочница Lua функции

0 голосов
/ 15 января 2019

Прежде чем начать, позвольте мне уточнить, что я просмотрел существующие вопросы, касающиеся песочницы, но ни одно из найденных решений не показалось мне подходящим.

То, что я пытаюсь реализовать, не обязательно является полностью безопасной песочницей для запуска небезопасного кода, но предоставляет среду, эмулирующую функции игры (если быть точнее, Star Wars Empire at War), я моддинг, который можно расширить используя сценарии Lua. Игра предоставляет набор глобальных функций, которые я хочу реализовать в этой среде, чтобы иметь возможность тестировать мои игровые расширения без запуска самой игры, поскольку ее возможности отладки весьма скудны. В игре также есть некоторые причуды. Актуальная проблема в этом случае заключается в том, что использование upvalues ​​приведет к повреждению сохраненных игр, поэтому наши модули Lua должны быть глобальными. Поскольку мои игровые сценарии не содержатся в одном файле и могут требовать других файлов, вызов и объявление их собственных функций с использованием setfenv() для установки окружения на самом деле не работает для меня, поскольку это относится только к указанной вами функции, а не к любые другие функции, которые он может вызвать.

Теперь для самого кода. До сих пор я пытался реализовать простую изолированную программную среду путем простого глубокого клонирования _G и восстановления его прежнего состояния после этого (это все еще WIP, я понимаю, что вы все еще можете вносить изменения в таблицу и среду или потенциально выходить из изолированной программной среды). которые не охватываются этим подходом; как я уже сказал, это не предназначено для запуска небезопасного кода).

Это в sandbox.lua:

local function run(func)
    -- tested deep_clone with luassert's assert.are_same(), so this should be fine
    local g_clone = deep_clone(_G)
    -- coroutine is needed because one of the game functions needs
    -- to be able to interrupt the script
    coroutine.wrap(function()
        func()
    end)()

    for k, v in pairs(_G) do
        if not g_clone[k] then
            _G[k] = nil
        else
            _G[k] = g_clone[k]
        end
    end
end

А это мой тестовый скрипт:

sandbox.run(function()
    require "src/declare_global" -- this declares A_GLOBAL = "TEST"
    print(A_GLOBAL) -- prints "TEST", everything is fine
end)

print(A_GLOBAL) -- prints nil, as intended

-- as I've restored _G to its former state I should be able to require 
-- the script again!
require "src/declare_global" 

print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- prints nil

print(A_GLOBAL) -- prints nil

Как вы видите, моя проблема в том, что требование того же файла, который просто объявляет глобальный после того, как я сделал это в своей песочнице, больше не работает. Так что, вероятно, есть проблема с восстановлением состояния _G, но я понятия не имею, что это может быть. Однако сам запрос вызова работает. Если нужный мне скрипт возвращает функцию, я все равно могу сохранить ее в переменной и выполнить.

Буду очень признателен за любые предложения, как это исправить.

1 Ответ

0 голосов
/ 15 января 2019

Я нашел решение своей проблемы.Тем не менее, я чувствую, что это неэффективный способ добиться того, чего я хочу, поэтому, если у кого-то есть лучшее предложение, я хотел бы услышать его._G указал мне правильное направление, поэтому я в итоге написал функцию «глубокого восстановления», которая, кажется, прекрасно работает с тем, что я тестировал до сих пор.

local known_tables = {}

local function deep_restore(tab, backup)
    known_tables[tab] = true
    for k, v in pairs(tab) do
        if backup[k] == nil then
            tab[k] = nil
        elseif type(v) == "table" and not known_tables[v] then
            deep_restore(v, backup[k])
        elseif not type(v) == "table" then
            tab[k] = backup[k]
        end
    end
end


local function run(func)
    local g_clone = deep_clone(_G)

    coroutine.wrap(function()
        func()
    end)()

    deep_restore(_G, g_clone)
    known_tables = {}

end

Результаты:

sandbox.run(function()
    require "src/declare_global"
    print("Printing inside sandbox: "..A_GLOBAL) -- prints TEST
end)

print("This should be nil: "..tostring(A_GLOBAL)) -- nil
print("package.loaded should be nil: "..tostring(package.loaded["src/declare_global"])) -- nil

print("\nRequiring again...")
require "src/declare_global"
print("package.loaded: "..tostring(package.loaded["src/declare_global"])) -- true
print(A_GLOBAL) -- TEST
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...