重新创建Lua 5.2中的setfenv()
如何在Lua 5.2中重新创建setfenv的功能?我有些困难理解您应该如何使用新的_ENV环境变量。
在Lua 5.1中,您可以使用setfenv轻松地隔离任何函数。
--# Lua 5.1
print('_G', _G) -- address of _G
local foo = function()
print('env', _G) -- address of sandbox _G
bar = 1
end
-- create a simple sandbox
local env = { print = print }
env._G = env
-- set the environment and call the function
setfenv(foo, env)
foo()
-- we should have global in our environment table but not in _G
print(bar, env.bar)
运行此示例将显示输出:
_G table: 0x62d6b0
env table: 0x635d00
nil 1
我想在Lua 5.2中重新创建此简单示例。以下是我的尝试,但它不像上面的示例那样工作。
--# Lua 5.2
local function setfenv(f, env)
local _ENV = env or {} -- create the _ENV upvalue
return function(...)
print('upvalue', _ENV) -- address of _ENV upvalue
return f(...)
end
end
local foo = function()
print('_ENV', _ENV) -- address of function _ENV
bar = 1
end
-- create a simple sandbox
local env = { print = print }
env._G = env
-- set the environment and call the function
foo_env = setfenv(foo, env)
foo_env()
-- we should have global in our envoirnment table but not in _G
print(bar, env.bar)
运行此示例将显示输出:
upvalue table: 0x637e90
_ENV table: 0x6305f0
1 nil
我知道有关此主题的其他几个问题,但它们似乎主要是处理加载动态代码(文件或字符串),在Lua 5.2中提供的新load函数很好地工作。我特别要求在沙箱中运行任意函数的解决方案,而无需使用debug库。根据Lua文档 ,我们不应该依赖它。
在 Lua5.2 中,一个需要沙盒化的函数需要指定自身。一个简单的方式是将 _ENV 作为参数传入函数中。
function(_ENV)
...
end
或者将其包裹在一个定义环境的函数中:
local mk_func(_ENV)
return function()
...
end
end
local f = mk_func({print = print})
然而,这种显式使用 _ENV 的方式对于沙盒限制并不常用,因为你并不能总是默认其他函数会有一个 _ENV 变量。在这种情况下,就要根据情况而定。如果你只是想从另一个文件中加载代码,那么像 load 和 loadfile 这样的函数通常会接收一个可选的环境参数,你可以用来进行沙盒限制。此外,如果你要加载的代码是一个字符串形式的,那么你可以使用字符串操作来自己添加 _ENV 变量(比如,将一个带有环境参数的函数包裹在其周围):
local code = 'return function(_ENV) return ' .. their_code .. 'end'
最后,如果你真的需要动态函数环境的操作,你可以使用 debug 库来改变函数内部的 upvalue 变量 _ENV。虽然使用 debug 库通常是不鼓励的,但是我认为如果所有其他选择均不适用(在这种情况下,改变函数的环境已经是一个比较神秘的黑科技了,使用 debug 库并不会更糟糕)。
这有点贵,但如果这对你很重要... 为什么不使用string.dump,并将函数重新load到正确的环境中呢?
function setfenv(f, env)
return load(string.dump(f), nil, nil, env)
end
function foo()
herp(derp)
end
setfenv(foo, {herp = print, derp = "Hello, world!"})()
要在Lua 5.2中重新创建setfenv / getfenv,可以执行以下操作:
如果不是setfenv,则为Lua 5.2
- 基于http://lua-users.org/lists/lua-l/2010-06/msg00314.html
-这假设f是一个函数
local function findenv(f)
local level = 1
重复
local name,value = debug.getupvalue(f,level)
如果名称=='_ENV'则返回级别,值
级别=级别+ 1
直到名称== nil
返回空 end
getfenv = function(f)return(select(2,findenv(f))or _G)end
setfenv = function(f,t)
local level = findenv(f)
如果级别则调试设置值(f,级别,t)
返回f end
end
RPFeltz的答案(load(string.dump(f)...))很聪明,可能适合您,但它不能处理具有upvalue(除了\ _ENV之外)的函数。
还有一个[compat-env](https://github.com/davidm/lua-compat-env)模块,它实现了Lua 5.1函数在Lua 5.2和反之亦然。
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?
- addEventListener 返回 nil Lua
- Lua中获取用户配置主目录的跨平台方法
在 Lua 5.2 中,如果没有使用 Lua 的 debug 库,无法更改函数的环境。一旦创建了一个函数,它的环境就确定下来了。想要修改函数环境的唯一方式就是修改其第一个 upvalue,这需要使用 debug 库。
Lua 5.2 中环境的一般概念是,环境应该被视为不可变的,除了通过巧妙手段(例如:debug 库)。你可以在一个环境中创建一个函数,一旦在该环境中创建,它就将一直处于该环境中。
这就是 Lua 5.1 中通常使用环境的方式,但是可以轻松地通过普通函数调用修改任何环境,因此 Lua 解释器已经移除了
setfenv。这样,用户代码无法在内部为它们自己的函数设置环境。因此,外部世界获得了一个安全区,但内部世界无法在该安全区内设置自己的安全区。Lua 5.2 机制使得在函数创建后修改环境更加困难,但它确实允许在创建期间设置环境。这使得你可以在安全区内建立更小的安全区。
所以,你真正想要的是像这样重新安排你的代码:
local foo; do local _ENV = { print = print } function foo() print('env', _ENV) bar = 1 end end现在
foo已经是一个安全区,这样,破坏安全区就更加困难了。可以想象,这引起了一些 Lua 开发者之间的争议。