修改Lua Chunk环境:Lua 5.2

时间:2013-12-23 00:28:04

标签: lua

据我了解,在Lua 5.2中,环境存储在名为_ENV的upvalues中。这让我在运行它之前修改了一个块的环境真的很混乱,但在加载它之后。

我想加载一个带有一些函数的文件,并使用chunk将这些函数注入各种环境。例如:

chunk = loadfile( "file" )

-- Inject chunk's definitions
chunk._ENV = someTable -- imaginary syntax
chunk( )

chunk._ENV = someOtherTable
chunk( )

这可能来自Lua吗?我可以找到修改此upvalue的唯一示例是with the C api(另一个example from C api),但我试图在Lua中执行此操作。这可能吗?

编辑:我不确定是否使用调试库接受答案。 docs state函数可能很慢。我这样做是为了提高效率,以便不必从字符串(或文件,甚至更糟)中解析整个块,只是为了将变量定义注入各种环境。

编辑:看起来这是不可能的:Recreating setfenv() in Lua 5.2

编辑:我认为我这样做的最好方法是绑定一个可以修改环境的C函数。虽然这是一种更烦人的方式。

编辑:我认为更自然的方法是将所有块加载到不同的环境中。这些可以通过设置引用块的全局副本的元表来由任何其他环境“继承”。这不需要在加载后进行任何upvalue修改,但仍允许具有这些函数定义的多个环境。

3 个答案:

答案 0 :(得分:4)

允许块在不同环境中运行的最简单方法是使其显式并让它接收环境。在块的顶部添加此行可实现此目的:

_ENV=...

现在,您可以随意致电chunk(env1)及以后chunk(env2)

在那里,没有debug魔法与upvalues。

虽然很明显你的块是否包含该行,但你可以在加载时添加它,通过编写一个合适的读取器函数,该函数首先发送该行,然后发送文件的内容。

答案 1 :(得分:1)

我不明白为什么你要避免使用调试库,而你很乐意使用C函数(在沙箱中也不可能。)

可以使用debug.upvaluejoin

完成
function newEnvForChunk(chunk, index)
  local newEnv = {}
  local function source() return newEnv end
  debug.upvaluejoin(chunk, 1, source, 1)
  if index then setmetatable(newEnv, {__index=index}) end
  return newEnv
end

现在加载任何这样的块:

local myChunk = load "print(x)"

它最初会继承封闭的_ENV。现在给它一个新的:

local newEnv = newEnvForChunk(myChunk, _ENV)

并为'x'插入一个值:

newEnv.x = 99

现在,当您运行块时,它应该会看到x的值:

myChunk()

=> 99

答案 2 :(得分:0)

如果您不想修改块(按照LHF的最佳答案),则有两种选择:

设置空白环境,然后将其环境动态更改为您的环境

function compile(code)
   local meta = {}
   local env = setmetatable({},meta)
   return {meta=meta, f=load('return '..code, nil, nil, env)}
end

function eval(block, scope)
   block.meta.__index=scope
   return block.f()
end

local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14

设置空白环境,然后每次用自己的颜色重新设置其值

function compile(code)
   local env = {}
   return {env=env, f=load('return '..code, nil, nil, env)}
end

function eval(block, scope)
   for k,_ in pairs(block.env) do block.env[k]=nil end
   for k,v in pairs(scope) do block.env[k]=v end
   return block.f()
end

local block = compile('a + b * c')
print(eval(block, {a=1, b=2, c=3})) --> 7
print(eval(block, {a=2, b=3, c=4})) --> 14

请注意,如果微优化很重要,则第一个选项的速度大约是_ENV=...的2%,而第二个选项的速度大约是8–9✕。