There is no simple answer (after all those years, still). I'll elaborate on alternatives.
1 Prof. of archeology's answer (and a bit of disassembly):
The answer to this ancient question lies within primodial one. Namely, luadec. It is probably that back then it was in downtime, but, at least as of now, there is an updated version, which handles lua 5.1-.3.
Additionally, the string.dump
provides not a complete gibberish, it gives a program code in raw bytes of machine instructions, you can see a better representation of those with luac -l -p <filename>
.
Vm codes are not documented well, but people did put something together here. Luajit has somewhat better documentation on its own set of instructions.
Rebuilding code from vm the instructions is what luadec does. In theory, you could also splice your own instructions right into dumped string.
However, whatever trick you do to bytecodes, it will experience incompatibilities between different interpreters, including different releases of lua itself.
2. Actually doing X
Converting function to a string is quite a peculiar desire (unless you are doing code generation, in which case you already have the string in the first place).
The "log and inject code" is indeed quite general X, which may warrant the Y to be solved. But single cases can be covered by single measures.
Lua is very flexible language, and for example you could trace the flow of the value x
in the example by making it an object:
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))
As in example above, you could inject behavior by altering the environment of the function. Namely, there I've redefined global function tonumber
. You might want to completely pack the function in the different environment:
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()
For older versions you'd have to handle upvalues that may have references to global functions you try to redefine. For newer versions, you'd have to handle the upvalues which would be lost during dump-load process.
3. Reading files
Finally, as others said, if you have access to the source, you can try finding the function definition from that. Unless it's a single function definition or single return file the task might end up being equivalent to re-implementation of the lua parser. There is more than a single of those, but they were not done with such functionality in mind so it might take some work to repurpose their code.
If all the functions are defined by yourself and you are willing to restrain yourself a little, you could use lua metatables again, solve the problem during the coding stage:
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))
Defining upvalues will be tricky, however.