Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dump lua function chunk to string?

Tags:

lua

lua-5.2

How to dump lua function chunk to string ?

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

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

I wish dumpToString result is following:

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

How to do this ?

=== update1 ===
I want to automatically log and inject code.

like image 293
Flash Avatar asked Feb 06 '13 03:02

Flash


2 Answers

You don't say why you want to do this, which may be important. You can dump a function to a string, it just won't be a very readable string; you can store and transmit your function this way (between compatible Lua engines):

string.dump(function() print "Hello" end)
like image 61
Paul Kulchenko Avatar answered Sep 23 '22 19:09

Paul Kulchenko


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.

like image 34
Dimitry Avatar answered Sep 22 '22 19:09

Dimitry