[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: Re: controlling library load
- From: Sean Conner <sean@...>
- Date: Fri, 23 Sep 2022 20:51:10 -0400
It was thus said that the Great Viacheslav Usov once stated:
> If anything like this has been done before, kindly point me to that.
>
> I have an application that hosts one or more lua_State instances. I
> want to control what libraries a given instance can load. For that
> purpose, I have disabled the default searchers (except the preload)
> and have my own searcher that decides what can be loaded. I have also
> replaced the global 'require' with my own function, which currently
> just calls (eventually) into the original require. So I can already
> control what libraries a given instance can load, no problem.
>
> Now I'd like to make this a bit more sophisticated. I want to be able
> to say that if library X can be loaded, then all the libraries that X
> loads directly or indirectly can also be loaded, without having to
> specify them upfront.
So I suppose you have something like:
do
local lua_require = require
local function new_require(modname)
if allowed_module(modname) then
return lua_require(modname)
else
error(string.format("module %s not allowed\n",modname))
end
end
require = new_require
end
This might do what you want, and cover all the cases:
do
-- --------------------------------------------------------------
-- Save the current _ENV into a table. This copy will have the
-- original Lua require function, and any allowed module that is
-- loaded will get this new environment allowing it to always load
-- any modules it wants.
--
-- The current _ENV will have a custom require that checks if a
-- module is allowed to be loaded.
-- --------------------------------------------------------------
local newenv = duptable(_ENV)
newenv.require = lua_require
local function new_require(modname)
if package.loaded[modname] then
return package.loaded[modname]
end
if allowed_module(modname) then
for _,loader in ipairs(package.searchers) do
local modfun,extra = loader(modname)
if type(modfun) == 'function' then
-- ---------------------
-- XXX might want to double check that _ENV ius always the
-- first upvalue. Or cycle through any upvalues, looking
-- for a table that is equal to the current _ENV. But
-- anyway, the allowed module will have access to the
-- actual Lua require, not our custom one.
-- ----------------------
debug.setupvalue(modfun,1,newenv)
local ret = modfun(modname,extra)
package.loaded[modname] = ret or true
return ret
end
end
end
error("This shouldn't happen if a module is allowed, should it?")
end
require = new_require
end
This will hopefully handle cases like the following:
-- a silly module that is allowed
local foo = require "foo" -- this is allowed
return {
bar = function()
local blah = require "bar" -- this is also allowed
-- rest of function
end
}
> For that I have to overcome a few hurdles. The biggest as it seems to
> me today is that I will have to understand that library Y is requested
> directly or indirectly by X (or not). I am not exactly sure how this
> can be done.
The above code appears to cover the indirect method, but it's untested
code, so use at your own risk. Hopefully, the code is clear enough in what
it's doing---which is calling the package.searchers[] directly, and
modifying the global environment of each module's loading function to
include the actual Lua require, not your custom one.
> In principle, I'd be interested in a design that works equally with
> native and pure Lua libraries. But one that works only with pure Lua
> libraries would also be a good step forward.
The trick I did above appears to work for Lua based modules [2], but I'm
unsure how well it would work for C based ones. It would probably be wise
to do more error checking than I did.
-spc (Hopefully this will point you in the right direction)
[1] I wanted to make a single executable out of a Lua application and
wanted to make sure all the modules it uses were included in the
executable. I used the code to log the modules as they were loaded.
[2] By manually doing:
fun,extra = package.searchers[2]("lua-based-module")
and checking the upvalues of the returned function fun and yup,
there's _ENV as the first upvalue. But:
fun,extra = package.searchers[3]("C-based-module")
shows fun having no upvalues. But I'm not aware of many C based
modules that require other modules directly.