ThePhD / sol2

Sol3 (sol2 v3.0) - a C++ <-> Lua API wrapper with advanced features and top notch performance - is here, and it's great! Documentation:
http://sol2.rtfd.io/
MIT License
4.12k stars 500 forks source link

Crash when iterating over state, and when iterating over globals. How to get a list of functions in a script? #1434

Closed sigmareaver closed 1 year ago

sigmareaver commented 1 year ago

I am using LuaJIT. I would like to iterate over the state and print out the names of all functions loaded in a script.

auto result = lua.load(R"STR(
    function Func1() print("Hello") end
    function Func2() print("World") end
    function Func3() print("Foo") end
    function Func4() print("Bar") end
)STR");
if (result.valid() && result().valid()) { // If loading code was success and if executing code was success
    for (auto it = lua.begin(); it != lua.end(); it++)
    {
        auto [key, value] = (*it);
        if (value.get_type() != sol::type::function)
            continue;
        sol::type key_type = key.get_type();
        sol::type value_type = value.get_type();
        std::cout << int(key_type) << ": " << key.as<std::string>() << " (" << int(value_type) << ")\n";
    }
}

I have tried the above code, as well as changing lua.begin() and lua.end() to globals.begin() and globals.end() where globals is a local table of lua.globals().

After it iterates through once, maybe twice, it crashes on it++. The stack being:

luaH_realasize(const Table * t=0x00000000) Line 251 C
luaH_next(lua_State * L=0x008e619c, Table * t=0x00000000, StackValue * key=0x008e6528) Line 348 C
lua_next(lua_State * L=0x008e619c, int idx=2) Line 1262 C
sol::basic_table_iterator<sol::basic_reference<0>>::operator++() Line 23316 C++
sol::basic_table_iterator<sol::basic_reference<0>>::operator++(int __formal=0) Line 23333   C++

The crash is a null pointer access violation, due to Table* t being NULL.

Am I doing something wrong? Or is there a workaround? How can I get a list of functions in a script?

sigmareaver commented 1 year ago

It seems that the solution is to use the built-in for_each function that comes with the state/table/where you need it.

However, this lists everything that's been previously loaded in and not just the items in the script that I desire. Running the script with an empty environment doesn't seem to do much either. I don't know if Sol has a solution for this, but my solution was to use lua_setupvalue(). Here is the example code:

auto result = m_lua_state.load(R"STR(
    function Func1() print("Hello") end
    function Func2() print("World") end
    function Func3() print("Foo") end
    function Func4() print("Bar") end
)STR");
m_lua_state.create_named_table("MyEnv");
lua_getglobal(m_lua_state.lua_state(), "MyEnv");
if (!lua_setupvalue(m_lua_state.lua_state(), -2, 1))
    std::cout << "lua_setupvalue() failed!\n";
sol::table MyEnv = m_lua_state["MyEnv"];
if (result.valid() && result().valid()) { // If loading code was success and if executing code was success
    std::cout << "Printing MyEnv...\n";
    MyEnv.for_each(
        [](std::pair<sol::object, sol::object> kv_pair) {
            auto type = kv_pair.second.get_type();
            std::cout << "   " << kv_pair.first.as<std::string>() << ": " << (int)type << "\n";
        }
    );
}

And here is the output:

Printing MyEnv...
   Func2: 6
   Func1: 6
   Func3: 6
   Func4: 6