Search code examples
luarequireluajit

Requiring a LuaJIT module in a subdir is overwriting a module of the same name in the parent dir


I have a file setup like this:

main.lua  (requires 'mydir.b' and then 'b')
b.lua
mydir/
  b.so    (LuaJIT C module)

From main, I do this:

function print_loaded()
  for k, v in pairs(package.loaded) do print(k, v) end
end

print_loaded()
require 'mydir.b'
print_loaded()

-- This would now include 'mydir.b' instead of 'b':
local b = require 'b'

The outputs of the prints show that my call to require 'mydir.b' is setting the return value as the value of package.loaded['b'] as well as the expected package.loaded['mydir.b']. I wanted to have package.loaded['b'] left unset so that I can later require 'b' and not end up with the (in my opinion incorrectly) cached value from mydir.b.

My question is: What's a good way to deal with this?

In my case, I want to be able to copy around mydir as a subdir of any of my LuaJIT projects, and not have to worry about mydir.whatever polluting the module namespace by destroying any later requires of whatever at the parent directory level.

In anticipation of people saying, "just rename your modules!" Yes. I can do that. But I'd love to know if there's a better solution that allows me to simply not have to worry about the name collisions at all.


Solution

  • The problem was that I was calling luaL_register incorrectly from within my b.so's source file (b.c).

    Here is the bad code that caused the problem:

    static const struct luaL_reg b[] = {
      /* set up a list of function pointers here */
    };
    
    int luaopen_mydir_b(lua_State *L) {
      luaL_register(L, "b", b);  // <-- PROBLEM HERE (see below)
      return 1;                  // 1 = # Lua-visible return values on the stack.
    }
    

    The problem with the highlighted line is that it will specifically set package.loaded['b'] to have the return value of this module when it's loaded. This can be fixed by replacing the line with this:

    luaL_register(L, "mydir.b", b);
    

    which will set package.loaded['mydir.b'] instead, and thus leave room for later use of a module with the same name (without the mydir prefix).

    I didn't realize this until long after I asked this question, when I finally got around to reading the official docs for luaL_register for Lua 5.1, which is the version LuaJIT complies with.