New interface to function 'luaL_openselectedlibs'

Instead of preloading all non-loaded libraries, there is another
mask to select which libraries to preload.
This commit is contained in:
Roberto Ierusalimschy 2024-02-15 11:17:39 -03:00
parent c8121ce34b
commit 165389b27b
8 changed files with 80 additions and 56 deletions

22
linit.c
View File

@ -21,12 +21,12 @@
/*
** Standard Libraries
** Standard Libraries. (Must be listed in the same ORDER of their
** respective constants LUA_<libname>K.)
*/
static const luaL_Reg stdlibs[] = {
{LUA_GNAME, luaopen_base},
{LUA_LOADLIBNAME, luaopen_package},
{LUA_COLIBNAME, luaopen_coroutine},
{LUA_DBLIBNAME, luaopen_debug},
{LUA_IOLIBNAME, luaopen_io},
@ -35,30 +35,28 @@ static const luaL_Reg stdlibs[] = {
{LUA_STRLIBNAME, luaopen_string},
{LUA_TABLIBNAME, luaopen_table},
{LUA_UTF8LIBNAME, luaopen_utf8},
{NULL, NULL}
};
/*
** require selected standard libraries and add the others to the
** preload table.
** require and preload selected standard libraries
*/
LUALIB_API void luaL_openselectedlibs (lua_State *L, int what) {
int mask = 1;
LUALIB_API void luaL_openselectedlibs (lua_State *L, int load, int preload) {
int mask;
const luaL_Reg *lib;
luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE);
for (lib = stdlibs; lib->func; (lib++, mask <<= 1)) {
if (what & mask) { /* selected? */
for (lib = stdlibs, mask = 1; lib->name != NULL; lib++, mask <<= 1) {
if (load & mask) { /* selected? */
luaL_requiref(L, lib->name, lib->func, 1); /* require library */
lua_pop(L, 1); /* remove result from the stack */
}
else { /* add library to PRELOAD table */
else if (preload & mask) { /* selected? */
lua_pushcfunction(L, lib->func);
lua_setfield(L, -2, lib->name);
lua_setfield(L, -2, lib->name); /* add library to PRELOAD table */
}
}
lua_assert((mask >> 1) == LUA_UTF8LIBK);
lua_pop(L, 1); // remove PRELOAD table
lua_pop(L, 1); /* remove PRELOAD table */
}

View File

@ -1223,8 +1223,9 @@ static lua_State *getstate (lua_State *L) {
static int loadlib (lua_State *L) {
lua_State *L1 = getstate(L);
int what = luaL_checkinteger(L, 2);
luaL_openselectedlibs(L1, what);
int load = luaL_checkinteger(L, 2);
int preload = luaL_checkinteger(L, 3);
luaL_openselectedlibs(L1, load, preload);
luaL_requiref(L1, "T", luaB_opentests, 0);
lua_assert(lua_type(L1, -1) == LUA_TTABLE);
/* 'requiref' should not reload module already loaded... */

2
lua.c
View File

@ -618,7 +618,7 @@ static void doREPL (lua_State *L) {
/* }================================================================== */
#if !defined(luai_openlibs)
#define luai_openlibs(L) luaL_openlibs(L)
#define luai_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
#endif

View File

@ -14,11 +14,11 @@
/* version suffix for environment variable names */
#define LUA_VERSUFFIX "_" LUA_VERSION_MAJOR "_" LUA_VERSION_MINOR
#define LUA_GK 1
#define LUA_GLIBK 1
LUAMOD_API int (luaopen_base) (lua_State *L);
#define LUA_LOADLIBNAME "package"
#define LUA_LOADLIBK (LUA_GK << 1)
#define LUA_LOADLIBK (LUA_GLIBK << 1)
LUAMOD_API int (luaopen_package) (lua_State *L);
@ -56,10 +56,10 @@ LUAMOD_API int (luaopen_utf8) (lua_State *L);
/* open selected libraries */
LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int what);
LUALIB_API void (luaL_openselectedlibs) (lua_State *L, int load, int preload);
/* open all libraries */
#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0)
#define luaL_openlibs(L) luaL_openselectedlibs(L, ~0, 0)
#endif

View File

@ -358,7 +358,7 @@ item = function (s)
local t, p = string.match(s, "^([^\n|]+)|()")
if t then
s = string.sub(s, p)
s = Tag.b(t..": ") .. s
s = Tag.b(t) ..": " .. s
end
return Tag.li(fixpara(s))
end,

View File

@ -664,7 +664,6 @@ Values equal to or less than 100 mean the collector will not wait to
start a new cycle.
A value of 200 means that the collector waits for
the total number of objects to double before starting a new cycle.
The default value is 200.
The garbage-collector step size controls the
size of each incremental step,
@ -672,7 +671,6 @@ specifically how many objects the interpreter creates
before performing a step:
A value of @M{n} means the interpreter will create
approximately @M{n} objects between steps.
The default value is 250.
The garbage-collector step multiplier
controls the size of each GC step.
@ -681,7 +679,6 @@ in each step, @M{n%} objects for each created object.
Larger values make the collector more aggressive.
Beware that values too small can
make the collector too slow to ever finish a cycle.
The default value is 200.
As a special case, a zero value means unlimited work,
effectively producing a non-incremental, stop-the-world collector.
@ -711,7 +708,6 @@ after the last major collection.
For instance, for a multiplier of 20,
the collector will do a minor collection when the number of objects
gets 20% larger than the total after the last major collection.
The default value is 25.
The minor-major multiplier controls the shift to major collections.
For a multiplier @M{x},
@ -721,7 +717,6 @@ than the total after the previous major collection.
For instance, for a multiplier of 100,
the collector will do a major collection when the number of old objects
gets larger than twice the total after the previous major collection.
The default value is 100.
The major-minor multiplier controls the shift back to minor collections.
For a multiplier @M{x},
@ -731,7 +726,6 @@ of the objects allocated during the last cycle.
In particular, for a multiplier of 0,
the collector will immediately shift back to minor collections
after doing one cycle of major collections.
The default value is 50.
}
@ -5885,13 +5879,6 @@ or @id{NULL} if there is a @x{memory allocation error}.
}
@APIEntry{void luaL_openlibs (lua_State *L);|
@apii{0,0,e}
Opens all standard Lua libraries into the given state.
}
@APIEntry{
T luaL_opt (L, func, arg, dflt);|
@apii{0,0,-}
@ -6073,7 +6060,7 @@ and sets the call result to @T{package.loaded[modname]},
as if that function has been called through @Lid{require}.
If @id{glb} is true,
also stores the module into the global @id{modname}.
also stores the module into the global variable @id{modname}.
Leaves a copy of the module on the stack.
@ -6290,23 +6277,61 @@ Except for the basic and the package libraries,
each library provides all its functions as fields of a global table
or as methods of its objects.
To have access to these libraries,
the @N{C host} program should call the @Lid{luaL_openlibs} function,
which opens all standard libraries.
}
@sect2{lualib-h| @title{Loading the Libraries in C code}
A @N{C host} program must explicitly load
the standard libraries into a state,
if it wants its scripts to use them.
For that,
the host program can call the function @Lid{luaL_openlibs}.
Alternatively,
the host program can open them individually by using
@Lid{luaL_requiref} to call
@defid{luaopen_base} (for the basic library),
@defid{luaopen_package} (for the package library),
@defid{luaopen_coroutine} (for the coroutine library),
@defid{luaopen_string} (for the string library),
@defid{luaopen_utf8} (for the UTF-8 library),
@defid{luaopen_table} (for the table library),
@defid{luaopen_math} (for the mathematical library),
@defid{luaopen_io} (for the I/O library),
@defid{luaopen_os} (for the operating system library),
and @defid{luaopen_debug} (for the debug library).
These functions are declared in @defid{lualib.h}.
the host can select which libraries to open,
by using @Lid{luaL_openselectedlibs}.
Both functions are defined in the header file @id{lualib.h}.
@index{lualib.h}
The stand-alone interpreter @id{lua} @see{lua-sa}
already opens all standard libraries.
@APIEntry{void luaL_openlibs (lua_State *L);|
@apii{0,0,e}
Opens all standard Lua libraries into the given state.
}
@APIEntry{void luaL_openselectedlibs (lua_State *L, int load, int preload);|
@apii{0,0,e}
Opens (loads) and preloads selected libraries into the state @id{L}.
(To @emph{preload} means to add
the library loader into the table @Lid{package.preload},
so that the library can be required later by the program.
Keep in mind that @Lid{require} itself is provided
by the @emph{package} library.
If a program does not load that library,
it will be unable to require anything.)
The integer @id{load} selects which libraries to load;
the integer @id{preload} selects which to preload, among those not loaded.
Both are masks formed by a bitwise OR of the following constants:
@description{
@item{@defid{LUA_GLIBK} | the basic library.}
@item{@defid{LUA_LOADLIBK} | the package library.}
@item{@defid{LUA_COLIBK} | the coroutine library.}
@item{@defid{LUA_STRLIBK} | the string library.}
@item{@defid{LUA_UTF8LIBK} | the UTF-8 library.}
@item{@defid{LUA_TABLIBK} | the table library.}
@item{@defid{LUA_MATHLIBK} | the mathematical library.}
@item{@defid{LUA_IOLIBK} | the I/O library.}
@item{@defid{LUA_OSLIBK} | the operating system library.}
@item{@defid{LUA_DBLIBK} | the debug library.}
}
}
}

View File

@ -546,9 +546,9 @@ do
]], source)
collectgarbage()
local m2 = collectgarbage"count" * 1024
-- load used fewer than 350 bytes. Code alone has more than 3*N bytes,
-- load used fewer than 400 bytes. Code alone has more than 3*N bytes,
-- and string literal has N bytes. Both were not loaded.
assert(m2 > m1 and m2 - m1 < 350)
assert(m2 > m1 and m2 - m1 < 400)
X = 0; code(); assert(X == N and Y == string.rep("a", N))
X = nil; Y = nil
@ -1122,7 +1122,7 @@ assert(a == nil and c == 2) -- 2 == run-time error
a, b, c = T.doremote(L1, "return a+")
assert(a == nil and c == 3 and type(b) == "string") -- 3 == syntax error
T.loadlib(L1, 2) -- load only 'package'
T.loadlib(L1, 2, ~2) -- load only 'package', preload all others
a, b, c = T.doremote(L1, [[
string = require'string'
local initialG = _G -- not loaded yet
@ -1141,7 +1141,7 @@ T.closestate(L1);
L1 = T.newstate()
T.loadlib(L1, 0)
T.loadlib(L1, 0, 0)
T.doremote(L1, "a = {}")
T.testC(L1, [[getglobal "a"; pushstring "x"; pushint 1;
settable -3]])
@ -1524,7 +1524,7 @@ end
do -- garbage collection with no extra memory
local L = T.newstate()
T.loadlib(L, 1 | 2) -- load _G and 'package'
T.loadlib(L, 1 | 2, 0) -- load _G and 'package'
local res = (T.doremote(L, [[
_ENV = _G
assert(string == nil)

View File

@ -705,7 +705,7 @@ else
T.testC(state, "settop 0")
T.loadlib(state, 1 | 2) -- load _G and 'package'
T.loadlib(state, 1 | 2, 4) -- load _G and 'package', preload 'coroutine'
assert(T.doremote(state, [[
coroutine = require'coroutine';