Lua 5.1 setfenv()函数在全局空间中仍加载表。

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

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()
require("Singleton")
local test = {}

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

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

    Instance.var = "Init"
    return Instance
end

我希望的输出是:

nil      --(在运行沙盒代码之前在全局表中进行第一次检查)
nil      --(在运行沙盒代码之后在全局表中进行第二次检查)
Init     --(Singleton的变量的初始值)
Changed  --(在我们修改它后的Singleton的变量)
Init     --(另一个沙盒中的Singleton变量的初始值)
Changed  --(在不同沙盒中更改它后的Singleton的变量)

但是我得到的结果是:

nil
table: 05143108
Init
Changed
Changed
Changed

表明“sandbox()”将该表加载到全局空间中,尽管我使用“setfenv(sandbox,scriptTable)”将sandbox的环境设置为“scriptTable”,然后执行“sandbox()”。

我已经阅读了其他帖子中提到的沙盒示例,但是我仍然得到相同的结果。任何想法如何在不污染全局环境的情况下在其自己的环境中加载脚本?

点赞
用户3125367
用户3125367

你并没有真正污染全球环境,这里展示的是一个包系统的自然现象,即模块被缓存和共享,不取决于调用函数的环境。这允许单例模块工作,因为如果你不 require 它,而是使用 loadfile,它将被加载两次(并且比预期的单例要少得多)。

因此,如果真正的任务是在每个沙箱中仅加载模块一次,则可以在进入沙箱之前交换package.loadedpackage.preload和其他加载器状态变量。更多信息请参见 Lua 5.1 参考手册中的 Modules 部分。

使用 loadfile 的解决方案可能很好,但如果您计划在沙箱内部的复杂模块系统中跨模块要求,则确实会遇到大问题。

2014-02-13 08:55:43