通过pcall设置Lua函数环境,同时保留升值

时间:2018-07-31 19:44:07

标签: lua

我想在Lua(5.3)中调用一个函数,修改环境以注入其他可用的函数,并这样做(a)无需修改函数本身的代码,以及(b)使用{{1} }调用该函数,能够捕获错误。

例如:

foo.lua (不允许修改pcall的内容)

t:foo()

lib.lua (可以在此处进行任何更改以使其正常工作)

local t = require('lib')()

local b = 17
function t:foo()
    local a = 42
    print(a + b + c())
end

t()

我想修改以上内容,以在调用函数时将local lib = {} function lib.c() return 41 end local function go(t) for k,v in pairs(t) do if 'function'==type(v) then print(pcall(v)) end end end return function() return setmetatable({}, {__call=go}) end 注入到函数的lib链中,以便获得运行foo.lua的结果:

_ENV

而不是其当前输出:

100
true

我很确定我在5.1中完成了此工作(或等效的工作),但是false foo.lua:6: attempt to call a nil value (global 'c') 已经不存在了,我无法弄清楚如何为将要运行的现有功能修改环境,我无法修改其来源。 setfenv()的功能似乎非常有限,但我假设我缺少有关如何使用它的知识。

2 个答案:

答案 0 :(得分:1)

这对我有用。对您有用吗?

foo.lua

local L = require('lib')
local print=print
_ENV=L

local b = 17
function t:foo()
    local a = 42
    print(a + b + c())
end

t()

lib.lua

local lib = {}
function lib.c()
    return 41
end

local function go(t)
    for k,v in pairs(t) do
        if 'function'==type(v) then print(pcall(v)) end
    end
end

lib.t= setmetatable({}, {__call=go})

return lib

答案 1 :(得分:1)

通过this article来解决这个问题,它为getfenvsetfenv(依赖于debug库)提供了纯Lua替换。

完整代码如下,但是解决方案大致是:

setmetable(lib, {__index=getfenv(function_to_call)})
setfenv(function_to_call, lib)
pcall(function_to_call)

require 'debug'

local function setfenv(fn, env)
  local i = 1
  while true do
    local name = debug.getupvalue(fn, i)
    if name == "_ENV" then
      debug.upvaluejoin(fn, i, (function()
        return env
      end), 1)
      break
    elseif not name then
      break
    end

    i = i + 1
  end

  return fn
end

local function getfenv(fn)
  local i = 1
  while true do
    local name, val = debug.getupvalue(fn, i)
    if name == "_ENV" then
      return val
    elseif not name then
      break
    end
    i = i + 1
  end
end

local lib = {}
function lib:c()
    return 41
end
setmetatable(lib,{})

local function go(t)
    for k,v in pairs(t) do
        if 'function'==type(v) then
            getmetatable(lib).__index=getfenv(v)
            setfenv(v,lib)
            print(pcall(v))
        end
    end
end

return function()
    return setmetatable({}, {__call=go})
end