【问题标题】:How to dump lua function chunk to string?如何将lua函数块转储到字符串?
【发布时间】:2013-02-06 03:21:09
【问题描述】:

如何将 lua 函数块转储为字符串?

function test(a, b)
  local c = a + b
  return c
end

print( type(test) )  --> function
print( test )         --> function: 0053B108
print( dumpToString(test) )

我希望 dumpToString 结果如下:

function test(a, b)
  local c = a + b
  return c
end

如何做到这一点?

=== 更新1 ===
我想自动记录和注入代码。

【问题讨论】:

    标签: lua lua-5.2


    【解决方案1】:

    你没有说为什么你想这样做,这可能很重要。您可以将函数转储为字符串,但它不会是一个非常易读的字符串;您可以通过这种方式存储和传输您的函数(在兼容的 Lua 引擎之间):

    string.dump(function() print "Hello" end)
    

    【讨论】:

      【解决方案2】:

      没有简单的答案(经过这么多年,仍然如此)。我会详细说明替代方案。

      1 考古学教授的回答(还有一点拆解):

      这个古老问题的答案在于primodial one。即,luadec。可能那时它处于停机状态,但是,至少到目前为止,有一个updated version,它处理lua 5.1-.3。

      此外,string.dump 提供的不是完整的胡言乱语,它以机器指令的原始字节提供程序代码,您可以看到luac -l -p <filename> 的更好表示。 VM 代码没有很好地记录,但人们确实将一些东西放在一起here。 Luajit 在自己的指令集上有更好的文档。

      从 vm 指令重建代码是 luadec 所做的。理论上,您也可以将自己的指令直接拼接到转储字符串中。

      但是,无论您对字节码采取什么技巧,都会遇到不同解释器之间的不兼容问题,包括 lua 本身的不同版本。

      2。其实在做X

      将函数转换为字符串是一种非常特殊的愿望(除非您正在生成代码,在这种情况下,您首先已经有了字符串)。

      “记录并注入代码”确实是相当一般的 X,这可能保证 Y 得到解决。但是单一的案例可以通过单一的措施来涵盖。 Lua 是一种非常灵活的语言,例如,您可以通过将其设为对象来跟踪示例中值 x 的流动:

      local to2number = tonumber
      tonumber= function(o)
          local r= to2number(o) 
          if not r then
              local m= getmetatable(o)
              if m and m.__tonumber then
                  r=m.__tonumber(o)
              end
          end
          return r
      end
      local number
      number={
          new=function(n)
              return setmetatable({n},number)
          end,
          __add=function(me,other)
              print("I'm "..tostring(me).." and I'm being added to "..tostring(other))
              local o=tonumber(other) 
              return number.new(me[1]+o)
          end,
          __tonumber=function(me) return me[1] end,
          __tostring=function(me) return tostring(me[1]) end,
      }
      test(number.new(4), number.new(10))
      

      如上例所示,您可以通过改变函数的环境来注入行为。也就是说,我重新定义了全局函数tonumber。您可能希望在不同的环境中完全打包函数:

      local test = function() print"hello" end
      local newenv={print=function(s) print(s..'world')  end}
      setfenv(test,newenv)--this is lua 5.1, luajit, good luck with upvalues
      local test = load(string.dump(test),nil,nil,newenv)--this is lua 5.2-5.3, good luck with upvalues
      test()
      

      对于旧版本,您必须处理可能引用您尝试重新定义的全局函数的上值。对于较新的版本,您必须处理在转储加载过程中会丢失的 upvalue。

      3.读取文件

      最后,正如其他人所说,如果您可以访问源代码,您可以尝试从中找到函数定义。除非它是单个函数定义或单个返回文件,否则任务最终可能等同于重新实现 lua 解析器。其中不止一个,但它们并没有考虑到这样的功能,因此可能需要一些工作来重新利用它们的代码。

      如果所有的函数都是自己定义的,你愿意克制一点,你可以再用lua metatables,在编码阶段解决问题:

      local def=function(code,env)
          env=env or _ENV
          local compiled,q=load("return "..code,nil,nil,env)
          if not compiled then error(q) end
          local f=compiled()
          return setmetatable({code=code},{__call=function(me,...) return  f(...)  end})
      end
      
      local test=def[[function(a,b)
          return a+b
      end]]
      
      print(test(2,3))
      

      但是,定义上值会很棘手。

      【讨论】:

      • 详细编译。感谢您将所有内容汇总到一个地方。
      【解决方案3】:

      你没有。 Lua 不会将已编译的 Lua 脚本作为原始文本存储在任何地方。而且,由于它旨在成为一种小型脚本语言,因此它也没有提供反编译自己的字节码的机制。

      【讨论】:

      • -1。不正确。 Lua 中不存在这样的内置函数,但可以通过解析源文件(对于程序源代码中明确定义的函数)和拦截 require、load 和 loadfile 函数(对于函数在程序执行期间动态创建)。不过,这个想法的实现可能并不那么简单。
      • @EgorSkriptunoff:我的回答仍然正确,因为 Lua 不处理这些事情。是的,显然你可以做到,但这很棘手。即使对您来说,因为您在函数列表中错过了dofile。此外,如果源代码是预编译的,它也不起作用。所以不,这种方法通常也不起作用。简而言之,你不能。
      • @EgorSkriptunoff:另外,报复性的投票是微不足道的。
      • 感谢您对dofile 的更正。但我在主要问题上不同意你的看法。请注意,OP 没有问“Lua 本身是否处理这些事情?”。问题是“如何做到这一点?”,这意味着程序员的一些努力。对于 Lua 这样强大的工具,您的回答似乎过于悲观。
      • @EgorSkriptunoff:事实是你的建议不起作用。不是一般。它可能在某些特定情况下起作用,在这些情况下,您:a) 碰巧有一个 Lua 解析器。 b) 正在使用非预编译的 Lua 源。 c) 没有在应用程序中嵌入 Lua。即便如此,您也没有解释如何实际找到有问题的函数,因为函数对象和源代码中的命名函数之间没有直接关联(这是 OP 要求的:如何获取函数 object 并获取其来源)。简而言之,你不能一般这样做。
      【解决方案4】:

      好吧,您可以将多行代码全部存储在一个字符串变量中。只需使用双方括号代替引号即可。

      chunk = [[
      function test(a, b)
        local c = a + b
        return c
      end
      ]]
      

      【讨论】:

        【解决方案5】:

        您可以通过

        获取您的程序的源代码
        local source_code = io.open(arg[0]):read'*a'
        

        并对其进行解析以找到您的函数定义。
        它只适用于从命令行运行 lua 并将其作为参数传递的源文件,而不是字节码文件。

        【讨论】:

        • 解析 source_code 对我来说非常辛苦。你有 parse lua 代码库吗?
        • -1:不正确。这会加载源文件;它不需要特定的函数并将其转换为源代码。
        猜你喜欢
        • 1970-01-01
        • 2013-12-30
        • 2013-04-06
        • 2014-12-03
        • 2016-02-02
        • 1970-01-01
        • 2014-10-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多