1

我一直在尝试使用 setfenv() 以便将一个块加载到全局环境之外的环境中,但我遇到了一些麻烦。以下是我正在运行的代码:

-- main.lua
function SandboxScript(scriptTable, scriptName)
    setmetatable(scriptTable, { __index = _G })
    local sandbox = loadfile(scriptName)
    setfenv(sandbox, scriptTable)
    sandbox()
    return scriptTable
end

local function main()
    print(Singleton)
    local test = {}
    local single1 = SandboxScript(test, "C:\\pathto\\TestTable.lua")
    print(Singleton)
    test.Update()

    local test2 = {}
    local single2 = SandboxScript(test2, "C:\\pathto\\TestTable.lua")
    test2.Update()
end
main()

-- TestTable.lua
require("Singleton")
local test = {}

function Update()
    test = Singleton:new()
    print(test.var)
    test.var = "Changed"
    print(test.var)
end

-- Singleton.lua
Singleton = {}
Instance = {}

function Singleton:new()
    if(next(Instance)) then
        return Instance
    end

    Instance.var = "Init"
    return Instance
end

我期望这个的输出是:

nil      --(First check in global table before running sandbox code)
nil      --(Second check in global table after running sandbox code)
Init     --(Initial value of the Singleton's var)
Changed  --(Singleton's var after we change it)
Init     --(Initial value of the Singleton's var in a different sandbox)
Changed  --(Singleton's var after we change it in the different sandbox)


相反,我得到:

nil
table: 05143108
Init
Changed
Changed
Changed

表明“sandbox()”正在将表加载到全局空间中,即使我在执行“sandbox()”之前使用“setfenv(sandbox, scriptTable)”将沙盒的环境设置为“scriptTable”。

我已经浏览了其他帖子中提到的沙盒示例,但我仍然得到相同的结果。知道我可以做些什么来在它自己的环境中加载脚本而不污染全局环境吗?

4

1 回答 1

1

您并没有真正污染全局环境,您在这里看到的是包系统的本质,即每次调用都会缓存和共享模块require,而不取决于调用函数的环境。这允许 Singleton 模块工作,因为如果您不require这样做,但这样做loadfile,它将加载两次(并且比预期的要少得多)。

因此,如果真正的任务是每个沙箱只加载一次模块,那么您可以在进入沙箱之前交换package.loaded和其他加载器状态变量。package.preload更多信息在ModulesLua 5.1 参考手册部分。

解决方案loadfile可能很好,但是如果您打算在沙箱内的复杂模块系统中交叉需求模块,这确实会导致一个大问题。

于 2014-02-13T08:55:43.387 回答