在Lua 5.2中重新创建setfenv()

Ada*_*dam 11 lua

如何重新创建setfenvLua 5.2中的功能?我很难理解你应该如何使用新的_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)
Run Code Online (Sandbox Code Playgroud)

运行此示例显示输出:

_G    table: 0x62d6b0
env   table: 0x635d00
nil   1
Run Code Online (Sandbox Code Playgroud)



我想在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)
Run Code Online (Sandbox Code Playgroud)

运行此示例显示输出:

upvalue    table: 0x637e90
_ENV       table: 0x6305f0
1          nil
Run Code Online (Sandbox Code Playgroud)



我知道关于这个主题的其他几个问题,但它们似乎主要是处理加载动态代码(文件或字符串),使用loadLua 5.2中提供的新功能可以很好地工作.在这里,我特别要求在沙箱中运行任意函数的解决方案.我想在不使用debug库的情况下这样做.根据Lua 文档,我们不应该依赖它.

Nic*_*las 17

如果不使用Lua 5.2中的Lua调试库,则无法更改函数的环境.一旦创建了一个函数,那就是它所拥有的环境.修改此环境的唯一方法是修改其第一个upvalue,这需要调试库.

Lua 5.2中环境的一般概念是环境应该被认为是在欺骗之外不可变的(即:调试库).您在环境中创建一个函数; 一旦在那里创造,那就是它所拥有的环境.永远.

这就是环境在Lua 5.1中经常使用的方式,但是通过随意的函数调用来修改任何环境很容易并且受到制裁.如果删除了Lua解释器setfenv(以防止用户破坏沙箱),则用户代码无法在内部为自己的函数设置环境.所以外面的世界得到了一个沙箱,但是内部世界不能在沙箱有沙箱.

Lua 5.2机制使得在创建函数后修改环境变得更加困难,但它确实允许您创建期间设置环境.这让你在沙箱里面沙箱.

所以你真正想要的是像这样重新排列你的代码:

local foo;

do
  local _ENV = { print = print }

  function foo()
    print('env', _ENV)
    bar = 1
  end
end
Run Code Online (Sandbox Code Playgroud)

foo现在是沙箱.现在,有人打破沙箱要困难得多.

可以想象,这引起了Lua开发人员的一些争论.

  • Lua 5.2具有*lexical*环境,而不是Lua 5.1中的神奇动力环境.正如Nicol所提到的,如果一个人知道它在哪里,你仍然可以通过改变一个upvalue来改变一个函数的环境. (8认同)

RPF*_*ltz 12

它有点贵,但如果它对你来说那么重要......

为什么不使用string.dump,并将函数重新加载到正确的环境中?

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!"})()
Run Code Online (Sandbox Code Playgroud)


Pau*_*nko 11

setfenv/getfenv在Lua 5.2中重新创建,您可以执行以下操作:

if not setfenv then -- Lua 5.2
  -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
  -- this assumes f is a function
  local function findenv(f)
    local level = 1
    repeat
      local name, value = debug.getupvalue(f, level)
      if name == '_ENV' then return level, value end
      level = level + 1
    until name == nil
    return nil end
  getfenv = function (f) return(select(2, findenv(f)) or _G) end
  setfenv = function (f, t)
    local level = findenv(f)
    if level then debug.setupvalue(f, level, t) end
    return f end
end
Run Code Online (Sandbox Code Playgroud)

RPFeltz的答案(load(string.dump(f)...))是一个聪明的,可能适合你,但它不处理具有upvalues的函数(除了_ENV).

还有compat-env模块在Lua 5.2中实现Lua 5.1功能,反之亦然.