我一直在尝试使用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”。
我已经浏览了其他帖子中提到的Sand Boxes Example,但我仍然得到了相同的结果。知道如何在不污染全球环境的情况下在自己的环境中加载脚本吗?
答案 0 :(得分:1)
你并没有真正污染全球环境,你在这里看到的是包系统的本质,每次调用require
时都会缓存和共享模块,而不依赖于调用函数的环境。这允许Singleton模块工作,因为如果你不require
它,而是loadfile
,它将加载两次(并且非常少于预期的单例)。
因此,如果真正的任务是只加载模块一次每个沙箱 ,那么您可以交换package.loaded
,package.preload
和其他加载器进入沙箱之前的状态变量。有关Lua 5.1参考手册的Modules
部分的更多信息。
使用loadfile
的解决方案可能会很好,但是如果您计划在沙箱中的复杂模块系统中交叉需要模块,那么这将导致一个大问题。