Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

confusing behaviour of environments for lua > 5.2 #1604

Closed
lceWolf opened this issue May 23, 2024 · 1 comment
Closed

confusing behaviour of environments for lua > 5.2 #1604

lceWolf opened this issue May 23, 2024 · 1 comment

Comments

@lceWolf
Copy link

lceWolf commented May 23, 2024

This might not be an issue with sol2, but how lua itself works with environments, but here is the deal:

When loading a lua file via sol::script_file which contains e.g. two functions, setting the environment of one functions also changes the environment of the other. Same goes for when a call to sol::script defines two functions in one go.
Is this behaviour expected? (might be related to #1079 (comment))

The example below covers two scenarios (1 OK and 1 NOK):
First I create two functions with two seperate calls to sol::script. Setting the environment of one of these functions does not modify the environment of the other function - OK.
The second time I create the two functions using only one call to sol::script. Now, modifying the environment of one of these functions will change the environment of the other function too - NOK.

Personally, I expected the second case to behave just like the first one did.

I am completely off into the blue, but it looks like changing the environment for one functions, changes it for every function defined in that chunk 🤔

#include <sol/sol.hpp>

int main()
{
    sol::state lua;
    lua.open_libraries(sol::lib::base);

    sol::environment env(lua, sol::create, lua.globals());

    lua.script("function foo() print(x) end");
    lua.script("function other() print(x) end");

    sol::function foo = lua["foo"];
    sol::function other = lua["other"];

    lua.script(R"(
               function foo2()
                    print(x)
               end
               function other2()
                print(x)
               end)");

    sol::function foo2 = lua["foo2"];
    sol::function other2 = lua["other2"];

    lua["x"] = 99;
    env["x"] = 42;

    env.set_on(other);
    foo(); // -> 99 Okay
    other(); // -> 42 Okay

    std::cout << "###" << std::endl;

    env.set_on(other2);
    foo2(); // -> 42 NOT okay
    other2(); // -> 42 okay

    return 0;
}

A potential workaround for the issue:
• dump the bytecode of the to-be-isolated function using sol::function::dump
• load it again using sol::script(dump.as_string_view())
• set the environment on the newly-loaded function

@lceWolf
Copy link
Author

lceWolf commented May 23, 2024

http://lua-users.org/wiki/EnvironmentsTutorial confirms that the _ENV table is changed for everything in the current chunk

When loading a chunk, the top-level function gets a new _ENV upvalue, and any nested functions inside it can see it. You can pretend that when loading works something like this:

local _ENV = _G
return function (...) -- this function is what's returned from load
-- code you passed to load goes here, with all global variable names replaced with _ENV lookups
-- so, for example "a = b" becomes "_ENV.a = _ENV.b" if neither a nor b were declared local
end
Now you can see that _ENV is an ordinary local variable, how all the functions have access to the _ENV, and why if one function changes _ENV all other functions in the loaded chunks will see the change. That's why if you want a function to only change its own environment, you need to make a new _ENV local that shadows the original one.

@lceWolf lceWolf closed this as completed May 23, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant