Functions in lua tables - lua

I have
someTabe = {}
someTabe.foo = function (x,y)
return x + y
end
How can I get pint(function"(function (x,y) return x + y end)??? Not return result.

You cannot recover the source code of a function from inside Lua.
> print(someTabe.foo)
function: 0x7fed0bc091f0
This is telling you that someTabe.foo contains a function, which has been converted to internal representation stored at the address shown.
If you need to recover the source code of a function from inside Lua, you need to compile it manually with load and then use the debug library to get the source code.

You could could look for a decompiler online if you are just trying to see the code. However in your own code it is impossible.

You can't get code itself, but you can get bytecode using string.dump():
local f = function(x,y) print('AAA') end)
local bytecode = string.dump(f) -- Get bytecode of function
local f2 = load(f) -- It is copy of f, but (f ~= f2)
f2() -- prints AAA

Related

Distinguish function vs closure

Lua will write the code of a function out as bytes using string.dump, but warns that this does not work if there are any upvalues. Various snippets online describe hacking around this with debug. It looks like 'closed over variables' are called 'upvalues', which seems clear enough. Code is not data etc.
I'd like to serialise functions and don't need them to have any upvalues. The serialised function can take a table as an argument that gets serialised separately.
How do I detect attempts to pass closures to string.dump, before calling the broken result later?
Current thought is debug.getupvalue at index 1 and treat nil as meaning function, as opposed to closure, but I'd rather not call into the debug interface if there's an alternative.
Thanks!
Even with debug library it's very difficult to say whether a function has a non-trivial upvalue.
"Non-trivial" means "upvalue except _ENV".
When debug info is stripped from your program, all upvalues look almost the same :-)
local function test()
local function f1()
-- usual function without upvalues (except _ENV for accessing globals)
print("Hello")
end
local upv = {}
local function f2()
-- this function does have an upvalue
upv[#upv+1] = ""
end
-- display first upvalues
print(debug.getupvalue (f1, 1))
print(debug.getupvalue (f2, 1))
end
test()
local test_stripped = load(string.dump(test, true))
test_stripped()
Output:
_ENV table: 00000242bf521a80 -- test f1
upv table: 00000242bf529490 -- test f2
(no name) table: 00000242bf521a80 -- test_stripped f1
(no name) table: 00000242bf528e90 -- test_stripped f2
The first two lines of the output are printed by test(), the last two lines by test_stripped().
As you see, inside test_stripped functions f1 and f2 are almost undistinguishable.

Do the const and close keywords in Lua actually do anything?

I was excited to learn that, as of Lua 5.4, Lua supports constant (const) and to-be-closed (close) variables! However, upon testing these keywords, they don't seem to do anything at all. I wrote the following code to sample the features to get a better grasp of their exact usage:
function f()
local const x = 3
print(x)
x = 2
print(x)
end
f()
function g()
local close x = {}
setmetatable(x, {__close = function() print("closed!") end})
end
g()
I titled the file constCheck.lua and ran it with lua constCheck.lua. The output is as follows:
3
2
I was expecting an error on my call to f(), or at least for it to print 3 twice, instead it seemed to reassign x with no issue at all. Further, I was expecting the call to g() to print out "closed!" when x left scope at the end of the function, but this did not happen. I can't find very many examples of these keywords' usage. Am I using them properly? Do they work?
Note: lua -v => Lua 5.4.0 Copyright (C) 1994-2020 Lua.org, PUC-Rio
This is <const> not const, and <close> not close
See https://lwn.net/Articles/826134/
do
local x <const> = 42
x = x+1
end
-- ERROR: attempt to assign to const variable 'x'
And some example https://github.com/lua/lua/blob/master/testes/code.lua#L11
local k0aux <const> = 0
https://github.com/lua/lua/blob/master/testes/files.lua#L128
local f <close> = assert(io.open(file, "w"))
From the Lua 5.4 Reference Manual : 3.3.7 - Local Declarations
Each variable name may be postfixed by an attribute ( a name between angle brackets):
attrib ::= [‘<’ Name ‘>’]
There are two possible attributes: const, which declares a constant
variable, that is, a variable that cannot be assigned to after its
initialization; and close, which declares a to-be-closed variable
So you would have to write local x <const> = 3 for example.
Your code local const x = 3 is equivalent to
local const = nil
x = 3
So you're actually creating a local nil value const and a global number value x.

Use of _ENV in Lua function does not have effect

I'm reviewing some toy examples from Lua and I found the following one over there with respect to environments:
M = {} -- the module
complex = {} -- global complex numbers registry
mt = {} --metatable for complex numbers
function new (r, i)
local cp = {}
cp = {r=r, i=i}
return setmetatable(cp,mt)
end
M.new = new -- add 'new' to the module
function M.op (...)
--Why does not it work?
local _ENV = complex
return ...
end
function M.add (c1, c2)
return new(c1.r + c2.r, c1.i + c2.i)
end
function M.tostring (c)
return string.format("(%g,%g)", c.r, c.i) --to avoid +-
end
mt.__tostring = M.tostring
mt.__add = M.add
complex.a = M.new(4,3)
complex.b = N.new(6,2)
--nil
M.op(a+b)
--It works
M,op(complex.a+complex.b)
The use of _ENV has no effect. However, if I use complex = _G, both lines work. How do set a local environment for M.op. I'm not asking for specific libraries, I just want to know why it does not work and how to fix it.
M.op(a+b)
This line doesn't do what you expect, because it uses values of a and b that are available when this method is called. It doesn't matter that you set _ENV value inside the method, as by the time the control gets there, the values referenced by a and b have already been retrieved and since both values are nil in your code, you probably get "attempt to perform arithmetic on global..." error.
how to fix it.
I'm not sure what exactly you want to fix, as you already reference the example that works. If you assign complex.a you can't assume that a will have the same value without mapping complex table to _ENV.

How to merge clib functions into a table using LuaJIT and FFI?

I have a table/object defined in Lua. I'm trying to add some methods from a C-API dll. I could attach the methods one at a time, but there are a lot of them. The last line of the code below is how I would like to do it. It is supposed to merge the methods into the Utilities object so that I don't have to do them one at a time. I'm getting the following error:
bad argument #1 to 'pairs' (table expected, got userdata)" const char *
Here is some sample code:
Utilities = {}
--
-- Other Code that defines/attaches methods to Utilities
--
-- Define some methods from my utilities.dll
local ffi = require("ffi")
ffi.cdef[[
void LogThis(const char * format, ...);
]]
local utilities_ffi = ffi.load("utilities")
-- This works
utilities_ffi.LogThis("hello world")
-- merge the two tables together (this fails)
for k,v in pairs(utilities_ffi) do Utilities[k] = v end
FFI must be returning a userdata object.
FFI library objects don't support iteration; you can't run pairs over them. You'll have to write an assignment for each function manually.
Also keep in mind that it's faster to access C functions directly from the library object, rather than storing them in a table (or even a local variable) and accessing them there. See the last section of the FFI tutorial.
Try this:
local function get(C, k)
return C[k]
end
function merge(C1, C2)
return setmetatable({}, {__index = function(t, k)
local ok, ret = pcall(get, C, k)
local v = ok and ret or C2[k]
t[k] = v --cache it
end})
end
Utilities = merge(utilities_ffi, other_ffi)

Create Lua function from string

I am creating functions (of x) from a string in Lua. The code I am using is
function fcreate(fs)
return assert(loadstring("return function (x) return " .. fs.." end"))()
end
This works for globals, e.g.
u=fcreate("math.sin(x)")
does the right thing.
However, it does not seem to like local variables. So
local c=1
u=fcreate("math.sin(x)+c")
will not work because c is local.
Is this fixable?
"loadstring does not compile with lexical scoping", so no, it can't see locals outside the loadstring call.
Is this fixable?
That depends. Why are you using loadstring in the first place? Lua supports closures as first class values, so I can't see from your example why you'd need loadstring.
Your example:
u = fcreate("math.sin(x)+c")
Can be rewritten without the need for loadstring or your fcreate function:
u = function(x) return math.sin(x)+c end
Which of course is the same as:
function u(x) return math.sin(x) + c end
I can see a case for loadstring if you have user-configurable expressions that you wanted to compile into some other function, but your case with the local c suggests that's not the case. Are you trying to make some kinda of home-rolled lamda syntax?
Can't be done in any reasonable way. For an example of why, look at this:
function makefunction(name)
local a = 1
local b = 2
local c = 3
-- ...
return assert(loadstring("return " .. name))
end
local a = 4
local func = makefunction("a")
print(func())
If this worked, what is printed? 1 or 4? Does it capture the variable from the place where the function was loaded, even though that function doesn't exist anymore? Or does it look it up from the place where it was called?
The first would mean that the function is lexically scoped wherever it's created. Being able to access the variable after the function has exited means that the variable would need to be promoted into an upvalue dynamically, which is not something that Lua can do at the moment. As it is now, Lua can see every access to a local variable during compilation, so it knows which variables to turn into upvalues (at a performance hit) and which to keep as locals.
The second would mean that variable accesses inside a loadstring'd function would work completely different than every other access in Lua: Lua uses lexical scoping, not dynamic scoping. It'd be a huge implementation change in Lua, and an extremely inconsistent one.
So, neither is supported. You can control the environment of a dynamically loaded function, using setfenv in Lua 5.1 or the env parameter of load(...) in Lua 5.2, but neither of those let you access local variables automatically.
Something you could do if you don't need to mutate the local variables is to pass those values as arguments to the generated function. You would still need to manually specify the variables to close over but its better then nothing.
For example, you can build up your closure to look like
return (function(a,b,c)
return function(x) return print(a, x) end
end)(...)
We might do that by changing your function to look like
function fcreate(variables, fs)
local varnames = {}
local varvalues = {}
local nvars = 0
for n,v in pairs(variables) do
nvars = nvars + 1
table.insert(varnames, n)
table.insert(varvalues, v)
end
local chunk_str = (
'return (function(' .. table.concat(varnames, ',') .. ') ' ..
'return function(x) return ' .. fs .. ' end ' ..
'end)(...)'
)
return assert( loadstring(chunk_str) )( unpack(varvalues, 1, nvars) )
end
local a = 1;
local f = fcreate({a=a}, 'x+a')
print(f(1), f(2))

Resources