Прежде чем начать, позвольте мне уточнить, что я просмотрел существующие вопросы, касающиеся песочницы, но ни одно из найденных решений не показалось мне подходящим.
То, что я пытаюсь реализовать, не обязательно является полностью безопасной песочницей для запуска небезопасного кода, но предоставляет среду, эмулирующую функции игры (если быть точнее, 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, но я понятия не имею, что это может быть. Однако сам запрос вызова работает. Если нужный мне скрипт возвращает функцию, я все равно могу сохранить ее в переменной и выполнить.
Буду очень признателен за любые предложения, как это исправить.