Песочница Lua _ENV с личной базовой библиотекой (require, assert и т. д.)

Луа 5.3.2

У меня есть служба, написанная на Lua/C, которая выполняет файлы lua в том же Lua_State.

Мне нужно предоставить все стандартные библиотеки для среды выполнения файлов.

Самое простое - запускать файлы так: loadfile(file_path, "bt", _G)

Проблема в том, что код в файле может повредить глобальное состояние службы, поэтому этот метод небезопасен.

Итак, мне нужно создать изолированную среду loadfile(file_path, "bt", env)

Вопрос: как прописать все стандартные библиотеки из linit.c в переменной env?

Я могу просто прописать все либы из linit.c, кроме luaopen_base, потому что он содержит lua_pushglobaltable

Я подумал об этом:

local env = {}
for k,v in pairs(_G) do
    if type(v)=="function" then
        env[k] = v
    end
end

Но это выглядит жалким решением. У кого-нибудь есть лучшее решение?


person marsgpl    schedule 05.01.2016    source источник


Ответы (1)


Самый простой способ — заставить env наследоваться от _G:

setmetatable(env,{__index=_G})

Все в _G будет видно в env, но если вы запишете в env, создав глобальную переменную, это не повлияет на _G, что, похоже, вам и нужно.

К сожалению, сам _G виден в _G, и поэтому его можно использовать для записи в исходную среду, выполняя такие действия, как _G.print=anyvalue.

Чтобы защитить _G, добавьте:

env._G = env

К сожалению, оригинальный _G остается доступным через package.loaded._G. Это сложнее защитить, если вы хотите предоставить доступ к package. Самый простой способ — глубоко скопировать _G.package в env.package и изменить env.package.loaded._G=env.

person lhf    schedule 05.01.2016
comment
о, подождите, скрипт сможет сделать это: _G.package = 123, потому что _G будет передаваться по ссылке, когда я буду обращаться к нему так: _ENV._G - person marsgpl; 05.01.2016
comment
Не забудьте настроить поле __metatable, чтобы они не могли getmetatable(env).__index. - person warspyking; 05.01.2016