Lua 学习笔记:沙盒
November 5, 2012 分类:技术
背景知识
Lua 给我的感觉是:各种内置函数和标准库的存在感都是比较强的。如果执行这句:
1 | for name in pairs(_G) do print(_G) end |
就会把各种环境中已存在名称的打印出来:
- 全局变量:比如字符串
_VERSION 。 - 内置函数:比如
print 、tonumber 、dofile 之类。 - 模块名称:比如
string 、io 、coroutine 之类。
这里的全局变量
于是,平时对于全局变量的访问就可以等同于对
1 2 | value = _G[varname] --> value = varname _G[varname] = value --> varname = value |
改变函数的环境
函数的上下文环境可以通过
1 2 3 | a = 3 -- 全局变量 a setfenv(1, {}) -- 将当前函数的环境表改为空表 print(a) -- 出错,因为当前环境表中 print 已经不存在了 |
没错,不仅是
1 2 3 4 | a = 3 setfenv(1, { g = _G }) g.print(a) -- 输出 nil g.print(g.a) -- 输出 3 |
沙盒
于是,出于安全或者改变一些内置函数行为的目的,需要在执行 Lua 代码时改变其环境时便可以使用
1 2 3 4 5 6 7 8 | local env = {} -- 沙盒环境表,按需要添入允许的函数 function run_sandbox(code) local func, message = loadstring(code) if not func then return nil, message end -- 传入代码本身错误 setfenv(func, env) return pcall(func) end |
Lua 5.2 的 _ENV 变量
Lua 5.2 中所有对全局变量
Lua 的作者之一 Roberto Ierusalimschy 同志在介绍 Lua 5.2 时说:
the new scheme, with _ENV, allows the main benefit of setfenv with a little more than syntactic sugar.
就我的理解来说,优点就是原先虚无缥缈只能通过
于是以下两个函数内容大致是一样的:
1 2 3 4 5 6 7 8 9 10 11 | -- Lua 5.1 function foobar() setfenv(1, {}) -- code here end -- Lua 5.2 function foobar() local _ENV = {} -- code here end |
而更进一步的是,5.2 中对
1 | local func, message = load(code, nil, "t", env) |
参考
- http://lua-users.org/wiki/SandBoxes
- 云风的 Blog: lua 5.2 的 _ENV