Search code examples
lua

Lua: loadstring inside a function appearing to not work?


Inside a function (doloadstring), I noticed that loadstring, even when a correct string is passed, will not modify a function variable. What gives?

My guess is x is a local variable and loadstring only has global scope?`

print('this works as I expect')
x = 0
loadstring('x = 1')()
print(x)

print('this does not')
function doloadstring(x, s)
    print(x)
    loadstring(s)() -- loadstring does not appear to change a variable in a function, is it because x is local
    print(x)
    print(z)
end

doloadstring('0', 'x = 1') -- x is not changed
doloadstring('0', 'z = 1') -- z is created tho

This is the output:

this works as I expect
1
this does not
0
0
nil
0
0
1

Solution

  • Yes, the result is because that variable x doesn't exist in the environment that the function gets (the two are not connected in any way). There are functions like debug.upvaluejoin, debug.setupvalue, and debug.setlocal, but none of them will "connect" a local variable with a function upvalue to make them one and the same.

    You can do the following: (1) create a new environment, (2) populate it with the (values of) local variables and upvalues, (3) call the function with that environment (pass the environment to load for Lua 5.2+ or use setfenv in Lua 5.1), and then (4) take any changes from that environment and assign them back to the local variables/upvalues (you can use a proxy table to simplify the detection of the changes).

    You may check restore_vars and capture_vars functions from Mobdebug that do something like this. It solves the problem in the most generic case (when you don't have any information about variables that may be used in your "loaded" function), so if you have some information about it, you can definitely simplify the logic and pass a specific environment that matches what you need (you wouldn't need to capture all upvalues and local variables).