Discussion:
loadstring and accessing local variables
Gert
2009-12-11 20:26:24 UTC
Permalink
Hello,

Is there a way for dynamically loaded code to access local variables within a
function ? What I'm trying to do basically looks like this:

------------------------------------------------------------

var1 = "Hello"
local function one()
local var2 = "world"
local code = "print(var1, var2)"
chunk = loadstring(code)
chunk()
end

one()
------------------------------------------------------------

Since 'var2' is a local variable, the loadstring() chunk will not be able
to access it.

Thanks,
Petite Abeille
2009-12-11 20:30:18 UTC
Permalink
Post by Gert
Is there a way for dynamically loaded code to access local variables within a
function ?
Alternatively, take a look at setfenv.

Setting the environment of the loaded chunk before executing it would most likely allow you to achieve what you are after.
Gert
2009-12-11 20:46:07 UTC
Permalink
Post by Petite Abeille
Post by Gert
Is there a way for dynamically loaded code to access local variables
within a function ?
Alternatively, take a look at setfenv.
Setting the environment of the loaded chunk before executing it would
most likely allow you to achieve what you are after.
I've been playing around with that but no luck yet.

my first try was

setfenv(chunk, getfenv(1))

But this fails. My guess would be that getfenv() would get the
environment for the current function, but the environment does not
contain the local variables of the function itself, only the functions
in, well, it's environment.
Petite Abeille
2009-12-11 20:52:15 UTC
Permalink
Post by Gert
I've been playing around with that but no luck yet.
my first try was
setfenv(chunk, getfenv(1))
But this fails.
Right. The chunk doesn't have any environment at this point. You need to create one.

Here is an usage example:

http://dev.alt.textdrive.com/browser/HTTP/Data.lua#L63

local function Decode( anObject )
local aChunk = assert( loadstring( anObject ) )

setfenv( aChunk, { _ = {}, f = DecodeFunction } )
aChunk()

return getfenv( aChunk )._
end

In other words, provide '_' and 'f' to the chunk to use when executed.
Post by Gert
My guess would be that getfenv() would get the
environment for the current function, but the environment does not
contain the local variables of the function itself, only the functions
in, well, it's environment.
Right. You can use the environment to build an appropriate context for the chunk to execute in. What an "appropriate context" means, is up to you.
Gert
2009-12-11 20:56:42 UTC
Permalink
Post by Petite Abeille
Post by Gert
I've been playing around with that but no luck yet.
my first try was
setfenv(chunk, getfenv(1))
But this fails.
Right. The chunk doesn't have any environment at this point. You need to create one.
Post by Gert
My guess would be that getfenv() would get the
environment for the current function, but the environment does not
contain the local variables of the function itself, only the functions
in, well, it's environment.
Right. You can use the environment to build an appropriate context for the chunk to execute in. What an "appropriate context" means, is up to you.
In my application, the chunk is some generated lua code from some kind
of domain specific language. In this case, 'Appropriate context' would
everything reachable from the current function, just as if the loaded
chunk was running at the same location. Unfortunately things just don't
work that way, so I'll either have to use the debug library, or just
live with it.

I think I prefer the second options :)

Thanks,
Michal Kottman
2009-12-11 20:43:57 UTC
Permalink
Post by Gert
Hello,
Is there a way for dynamically loaded code to access local variables within a
I have tried to achieve the same thing, and basically ended up
collectiong all the locals and upvalues using functions from debug
table, and using setfenv() on the loaded code. The locals table has a
metatable with __index pointing to the environment of calling functions,
so it also has access to "globals".

Here is a proof of concept:

function eval(str)
local f = loadstring('return ' .. str)
if f then
local locals = {}
local caller = debug.getinfo(2, 'f').func
local i, k, v = 2, debug.getupvalue(caller, 1)
while k do
locals[k] = v
i, k, v = i+1, debug.getupvalue(caller, i)
end
i, k, v = 2, debug.getlocal(2, 1)
while k do
locals[k] = v
i, k, v = i+1, debug.getlocal(2, i)
end
local fenv = debug.getfenv(caller)
setmetatable(locals, { __index = fenv })
setfenv(f, locals)
return f()
end
end

X = 22

function dummy(x)
local x = 1
do
local x = 2
local y = x * x
x = x + 1
if x > 0 then
local x = 33
print(eval 'x * y + X')
end
end
end

dummy(10)
Post by Gert
154
I am not sure whether it handles all cases correctly, but for simple
purposes it should be enough.
Gert
2009-12-11 20:52:14 UTC
Permalink
Post by Michal Kottman
Post by Gert
Hello,
Is there a way for dynamically loaded code to access local variables within a
I have tried to achieve the same thing, and basically ended up
collectiong all the locals and upvalues using functions from debug
table,
Yes, I was afraid I would end up using the debug library. I just
realized locals are not part of the environment - which would be my
first intuitive guess - but live in registers instead.

Ok, nothing I can do about it then, thanks for your help!
Doug Rogers
2009-12-12 00:20:36 UTC
Permalink
Post by Gert
Post by Michal Kottman
I have tried to achieve the same thing, and basically ended up
collectiong all the locals and upvalues using functions from debug
table,
Yes, I was afraid I would end up using the debug library. I just
realized locals are not part of the environment - which would be my
first intuitive guess - but live in registers instead.
Ok, nothing I can do about it then, thanks for your help!
Well, if you know where you're loading the string then you might know
the local variables that are likely to be used by the loaded code. You
can put them in a table whose its metatable has an __index set to the
global table, like:

var1 = "Hello"
local function one()
local var2 = "world"
local code = "print(var1, var2)"
chunk = loadstring(code)
local locals = { var2 = var2, }
setfenv(chunk, setmetatable(locals, { __index = _G }))
chunk()
end

one()

But that would require a little bit of maintenance as the code changes.

Doug

______________________________________________________________________________________
The information contained in this email transmission may contain proprietary and business
sensitive information. If you are not the intended recipient, you are hereby notified that
any review, dissemination, distribution or duplication of this communication is strictly
prohibited. Unauthorized interception of this e-mail is a violation of law. If you are not
the intended recipient, please contact the sender by reply email and immediately destroy all
copies of the original message.

Any technical data and/or information provided with or in this email may be subject to U.S.
export controls law. Export, diversion or disclosure contrary to U.S. law is prohibited.
Such technical data or information is not to be exported from the U.S. or given to any foreign
person in the U.S. without prior written authorization of Elbit Systems of America and the
appropriate U.S. Government agency.

Loading...