Search code examples
c++luarequire

Crashes when calling `lua_getfield()` after overriding the require


This question is related to @Henri_Menke's answer from this question : How to get preloaded module name in C++

I'm trying to override the require function with my own version so I can get the preloaded module name inside Lua script.

Here's my code:

#include "lua.hpp"

void main()
{
    lua_State *L = luaL_newstate();
    luaL_openlibs(L);
    lua_settop(L, 0);
    luaL_dostring(L, "local require_original = require\n"
                     "function require(name, ...)\n"
                         "current_module = name\n"
                         "require_original(name, ...)\n"
                         "current_module = nil\n"
                     "end\n"); //if I comment out this chunk, it works fine
    luaL_dostring(L, "package.preload['test'] = function ()\n"
                         "local test = {}\n"
                         "print('While loading:', current_module)\n"
                         "function test.update() print('Updated!') end\n"
                         "return test\n"
                     "end\n");
    lua_getglobal(L, "require");
    lua_pushstring(L, "test");
    if (lua_pcall(L, 1, LUA_MULTRET, 0))
    {
        std::cout << "Error: " << lua_tostring(L, -1) << std::endl;
        lua_pop(L, 1);
    }
    int top = lua_gettop(L);
    lua_getfield(L, -1, "update"); //crashes here
    if (lua_isfunction(L, -1))
    {
        lua_pushnil(L);
        if (lua_pcall(L, 1, LUA_MULTRET, 0))
        {
            std::cout << "Error: " << lua_tostring(L, -1) << std::endl;
            lua_pop(L, 1);
        }
    }
    lua_close(L);
}

However, it crashes when calling lua_getfield(L, -1, "update");.

And it no longer crashes when I comment out the first chunk of Lua script(first luaL_dostring).

I don't understand why it crashes if I use my own version of require.

How can I fix this?


Solution

  • I forgot to return the module table from overridden require function. Sorry about that.

    #include <iostream>
    #include "lua.hpp"
    
    int main() {
        lua_State *L = luaL_newstate();
        luaL_openlibs(L);
        lua_settop(L, 0);
        luaL_dostring(L, "local require_original = require\n"
                         "function require(name, ...)\n"
                             "current_module = name\n"
                             "local val = table.pack(require_original(name, ...))\n"
                             "current_module = nil\n"
                             "return table.unpack(val,1,val.n)\n"
                         "end\n"); //if I comment out this chunk, it works fine
        luaL_dostring(L, "package.preload['test'] = function ()\n"
                             "local test = {}\n"
                             "print('While loading:', current_module)\n"
                             "function test.update() print('Updated!') end\n"
                             "return test\n"
                         "end\n");
        lua_getglobal(L, "require");
        lua_pushstring(L, "test");
        if (lua_pcall(L, 1, LUA_MULTRET, 0))
        {
            std::cout << "Error: " << lua_tostring(L, -1) << std::endl;
            lua_pop(L, 1);
        }
        int top = lua_gettop(L);
        lua_getfield(L, -1, "update"); //crashes here
        if (lua_isfunction(L, -1))
        {
            lua_pushnil(L);
            if (lua_pcall(L, 1, LUA_MULTRET, 0))
            {
                std::cout << "Error: " << lua_tostring(L, -1) << std::endl;
                lua_pop(L, 1);
            }
        }
        lua_close(L);
    }
    
    $ clang++ -Wall -Wextra -Wpedantic -I /usr/include/lua5.2 test.cpp -llua5.2
    test.cpp:28:9: warning: unused variable 'top' [-Wunused-variable]
        int top = lua_gettop(L);
            ^
    1 warning generated.
    $ ./a.out 
    While loading:  test
    Updated!