Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/lua/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Lua5.2中重新创建setfenv()_Lua - Fatal编程技术网

在Lua5.2中重新创建setfenv()

在Lua5.2中重新创建setfenv(),lua,Lua,如何在Lua5.2中重新创建setfenv的功能?我在准确理解如何使用新的\u ENV环境变量时遇到一些问题。 在Lua5.1中,您可以使用setfenv轻松地对任何函数进行沙箱处理 --# Lua 5.1 print('_G', _G) -- address of _G local foo = function() print('env', _G) -- address of sandbox _G bar = 1 end -- c

如何在Lua5.2中重新创建
setfenv
的功能?我在准确理解如何使用新的
\u ENV
环境变量时遇到一些问题。

在Lua5.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


我想在Lua5.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



我知道关于这个主题的其他几个问题,但它们似乎主要是处理加载动态代码(文件或字符串),使用Lua5.2中提供的新的
load
函数,动态代码(文件或字符串)工作得相当好。这里我特别要求提供一种在沙箱中运行任意函数的解决方案。我想在不使用
调试
库的情况下执行此操作。根据Lua,我们不应该依赖它。

如果不使用Lua5.2中Lua中的调试库,就不能更改函数的环境。一旦创建了一个函数,这就是它所处的环境。修改此环境的唯一方法是修改其第一个upvalue,这需要调试库

Lua5.2中的环境的总体思想是,环境应该被认为是在欺骗之外不可变的(即:调试库)。在环境中创建函数;一旦在那里创建,这就是它所拥有的环境。永远

这就是Lua5.1中经常使用的环境,但是通过一个偶然的函数调用来修改任何环境都是很容易的。如果您的Lua解释器删除了setfenv(以防止用户破坏沙箱),那么用户代码就不能在内部为自己的函数设置环境。所以外部世界有一个沙箱,但内部世界不能在沙箱中有一个沙箱

Lua5.2机制使创建函数后修改环境变得更加困难,但它确实允许您在创建过程中设置环境。它可以让你在沙箱中放置沙箱

因此,您真正想要的是像这样重新排列代码:

local foo;

do
  local _ENV = { print = print }

  function foo()
    print('env', _ENV)
    bar = 1
  end
end
foo
现在是沙盒。现在,有人要打破沙箱要困难得多


可以想象,这在Lua开发人员之间引起了一些争论。

在Lua5.2中,沙盒函数需要自己指定。可以使用的一个简单模式是让它接收
\u ENV
作为参数

function(_ENV)
    ...
end
或者将其包装在定义环境的内容中

local mk_func(_ENV)
    return function()
        ...
    end
end

local f = mk_func({print = print})
但是,这种显式使用
\u ENV
对于沙箱的用处不大,因为您不能总是假定其他函数将通过使用
\u ENV
变量进行协作。在这种情况下,这取决于你做什么。如果您只想从其他文件加载代码,则诸如
load
loadfile
之类的函数通常会收到一个可选的环境参数,您可以将其用于沙箱。此外,如果您尝试加载的代码是字符串格式的,您可以使用字符串操作自己添加
\u ENV
变量(例如,通过在函数周围包装一个ENV参数)


最后,如果确实需要动态函数环境操作,可以使用调试库更改
\u ENV
的函数内部upvalue。虽然通常不鼓励使用调试库,但我认为如果所有其他替代方案都不适用,这是可以接受的(我觉得在这种情况下,更改函数的环境已经是很深的巫毒魔法了,所以使用调试库不会更糟)

这有点贵,但是如果它对您如此重要的话

为什么不使用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!"})()

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

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
RPFeltz的答案(
load(string.dump(f)…
)是一个聪明的答案,可能对您有用,但它不处理具有upvalue的函数(除了_ENV)


Lua5.2中也有实现Lua5.1函数的模块,反之亦然。

Lua5.2有词汇环境,而不是Lua5.1中神奇的动态环境。正如Nicol提到的,如果知道函数的位置,仍然可以通过更改upvalue来更改函数的环境。谢谢您提供的信息。这是一种很好的方法,但似乎不可能对没有调试库而我没有编写的函数进行沙箱处理。@Adam:这些函数是从哪里来的?正如我所说的,您应该仍然能够在模块级而不是函数级进行沙箱处理。我正在寻找一种不用调试库的方法,但很高兴看到应该如何进行。谢谢。+1到目前为止,我在阅读手册时总是跳过string.dump。
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