关于沙盒:lua环境和模块

lua environments and modules

假设我有一个模块:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- env.lua

local env = {}

function env.resolve(str)
  print("mod", _ENV)

  if _resolve_path ~= nil then
    return _resolve_path(str)
  else
    error("bad env")
  end
end

return env

和一些使用它的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
-- sandbox demo
-- run as: lua env-test.lua

env = require('env')

function _resolve_path(path)
  return"/" .. path
end

print("before main()")
print("", _ENV)
print("", env.resolve("test"))

local sandbox
do
  local _ENV = {
    print = print,
    env = env,
    _resolve_path = function (path)
      return"/chroot/" .. path
    end
  }
  function sandbox()
    print("from sandbox()")
    print("", _ENV)
    print("", env.resolve("test"))
  end
end

sandbox()

print("after main()")
print("", _ENV)
print("", env.resolve("test"))

我想要实现的是sandbox()中的env.resolve()将使用环境中的自定义_resolve_path函数。 可以看出,该环境并未应用于从沙盒函数调用的代码。 目的是根据某些调用模块的位置来指导某些模块的行为。 例如。 具有具有不同本地_resolve_path()函数的沙箱{1,2,3}()。


当使用require加载模块时,它已绑定到全局环境。 在环境中创建功能后,它将在整个生命周期中都具有该环境。

在Lua 5.2之前,您可以使用set / getfenv来更改环境,但是现在环境是词汇化的。 只能使用调试库通过更改_ENV upvalue来更改环境。

那么,如何在不同的环境中运行相同的功能? 您可以将环境作为参数传递:

1
2
3
4
5
6
7
8
function env.resolve(str, _ENV)
  print("mod", _ENV)
  if _resolve_path ~= nil then
    return _resolve_path(str)
  else
    error("bad env")
  end
end

然后在其中调用resolve的地方,例如:

1
env.resolve('test', _ENV)

或者,如果您希望不必为每个resolve调用都指定环境,则可以将resolve函数绑定到每个新环境:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-- env.lua
local print = print
local error = error
local env = {}

-- this is the actual resolve function that takes the environment as a parameter
local function resolve_env(str, _ENV)
  print("mod", _ENV)
  if _resolve_path ~= nil then
    return _resolve_path(str)
  else
    error("bad env")
  end
end

-- this is the module (ie. global) resolve
function env.resolve(str)
  return resolve_env(str, _ENV)
end

-- this function binds a resolve function to a sandbox environment
function env.bind(_ENV)
  _ENV.env = {
    resolve = function(str)
      return resolve_env(str, _ENV)
    end
  }
  return _ENV
end

return env

沙盒现在可以设置绑定的解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
-- sandbox.lua
env = require 'env'

function _resolve_path(path)
  return"/" .. path
end

print("before main()")
print("", _ENV)
print("", env.resolve("test"))

local sandbox; do
  local _ENV = env.bind{
    print = print,
    _resolve_path = function (path)
      return"/chroot/" .. path
    end
  }

  function sandbox()
    print("from sandbox()")
    print("", _ENV)
    print("", env.resolve("test"))
  end
end

sandbox()

print("after main()")
print("", _ENV)
print("", env.resolve("test"))

这将产生结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
$ lua sandbox.lua
before main()
        table: 00612f40
mod     table: 00612f40
        /test
from sandbox()
        table: 0061c7a8
mod     table: 0061c7a8
        /chroot/test
after main()
        table: 00612f40
mod     table: 00612f40
        /test