How to dump lua function chunk to string?
Asked Answered
S

5

9

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.

Sin answered 6/2, 2013 at 3:21 Comment(0)
B
8

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)
Bluefarb answered 6/2, 2013 at 16:58 Comment(0)
B
7

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.

Brodench answered 16/5, 2017 at 21:46 Comment(1)
Detailed compilation. Thanks for summing it all in one place.Nepos
E
3

You don't. Lua doesn't store compiled Lua script as raw text anywhere. And, since it's intended to be a small scripting language, it also doesn't provide a mechanism to decompile its own bytecode.

Ervin answered 6/2, 2013 at 3:51 Comment(5)
-1. Incorrect. There exists no such built-it function in Lua, but one can achieve the same functionality by parsing source file (for functions defined explicitly in the program source code) and intercepting require, load and loadfile functions (for functions dynamically created during program execution). Though, the implementation of this idea may be not so simple.Bonis
@EgorSkriptunoff: My answer is still correct in that Lua doesn't handle these things. Yes, obviously you could do it, but it's tricky. Even for you, since you missed dofile in your list of functions. Also, it doesn't work if the source code is pre-compiled. So no, that method doesn't work generally either. In short, you can't.Ervin
@EgorSkriptunoff: Also, revenge downvoting is petty.Ervin
Thank you for correction about dofile. But I disagree with you in the main question. Please note that OP did not ask "does Lua itself handle these things?". The question was "How to do this?", wich imply some efforts from the programmer. Your answer seems to be too pessimistic for such a powerful tool as Lua.Bonis
@EgorSkriptunoff: The fact is that your suggestion doesn't work. Not in general. It might work in certain, specific cases, where you: a) happen to have a Lua parser around. b) are using non-precompiled Lua sources. c) are not embedding Lua in an application. And even then, you don't explain how to actually find the function in question, because there is no direct association between a function object and a named function in source code (which is what the OP asked for: how to take a function object and get its source). In short, you cannot generally do it.Ervin
B
1

well, you can store your multiple lines of code all in a single string variable. Simply use double square brackets instead of quotation marks.

chunk = [[
function test(a, b)
  local c = a + b
  return c
end
]]
Brickyard answered 12/2, 2013 at 6:32 Comment(0)
B
-2

You can get the source code of your program by

local source_code = io.open(arg[0]):read'*a'

and parse it to find your function definition.
It only works when running lua from command line and passing it source file as parameter, not a bytecode file.

Bonis answered 6/2, 2013 at 7:14 Comment(4)
parse source_code is very hard work for me. Do you have parse lua code library?Sin
-1: Incorrect. This loads the source file; it doesn't take a specific function and convert it into source.Ervin
Shouldn't this close the file handle?Moussorgsky
@LMD - The file handle will be closed soon by GC. As you see, the file handle is not anchored.Bonis

© 2022 - 2024 — McMap. All rights reserved.