我一直在尝试使用 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"。
我已经浏览了其他帖子中提到的沙盒示例,但我仍然得到相同的结果。知道我可以做些什么来在自己的环境中加载脚本而不会污染全局环境吗?
你并没有真正污染全局环境,你在这里看到的是包系统的一个性质,每次调用require
模块都会被缓存和共享,而不是依赖于调用函数的环境。这允许单例模块工作,因为如果你不require
它,而是loadfile
,它将加载两次(并且比预期的单例要少得多)。
因此,如果真正的任务是每个沙箱只加载一次模块,那么您可以在进入沙箱之前交换package.loaded
、package.preload
和其他加载器状态变量。更多信息Modules
请参阅 Lua 5.1 参考手册的章节。
loadfile
的解决方案可能很好,但是如果您计划在沙箱内的复杂模块系统中交叉要求模块,这确实会导致一个大问题。