没有简单的答案(经过这么多年,仍然如此)。我会详细说明替代方案。
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))
但是,定义上值会很棘手。