lua

A copy of the Lua development repository
Log | Files | Refs | README

commit ed2872cd3bf6d352f36bbd34529738a60b0b51eb
parent 2d3f09544895b422eeecf89e0d108da8b8fcdfca
Author: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date:   Wed, 17 Apr 2019 14:57:02 -0300

'require' returns where module was found

The function 'require' returns the *loader data* as a second result.
For file searchers, this data is the path where they found the module.

Diffstat:
Mloadlib.c | 23+++++++++++++++++------
Mlundump.c | 4++--
Mmanual/manual.of | 38+++++++++++++++++++++++++++-----------
Mtestes/attrib.lua | 35++++++++++++++++++++---------------
4 files changed, 66 insertions(+), 34 deletions(-)

diff --git a/loadlib.c b/loadlib.c @@ -576,9 +576,14 @@ static int searcher_Croot (lua_State *L) { static int searcher_preload (lua_State *L) { const char *name = luaL_checkstring(L, 1); lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); - if (lua_getfield(L, -1, name) == LUA_TNIL) /* not found? */ + if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ lua_pushfstring(L, "\n\tno field package.preload['%s']", name); - return 1; + return 1; + } + else { + lua_pushliteral(L, ":preload:"); + return 2; + } } @@ -620,17 +625,23 @@ static int ll_require (lua_State *L) { /* else must load package */ lua_pop(L, 1); /* remove 'getfield' result */ findloader(L, name); - lua_pushstring(L, name); /* pass name as argument to module loader */ - lua_insert(L, -2); /* name is 1st argument (before search data) */ + lua_rotate(L, -2, 1); /* function <-> loader data */ + lua_pushvalue(L, 1); /* name is 1st argument to module loader */ + lua_pushvalue(L, -3); /* loader data is 2nd argument */ + /* stack: ...; loader data; loader function; mod. name; loader data */ lua_call(L, 2, 1); /* run loader to load module */ + /* stack: ...; loader data; result from loader */ if (!lua_isnil(L, -1)) /* non-nil return? */ lua_setfield(L, 2, name); /* LOADED[name] = returned value */ + else + lua_pop(L, 1); /* pop nil */ if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ lua_pushboolean(L, 1); /* use true as result */ - lua_pushvalue(L, -1); /* extra copy to be returned */ + lua_copy(L, -1, -2); /* replace loader result */ lua_setfield(L, 2, name); /* LOADED[name] = true */ } - return 1; + lua_rotate(L, -2, 1); /* loader data <-> module result */ + return 2; /* return module result and loader data */ } /* }====================================================== */ diff --git a/lundump.c b/lundump.c @@ -271,8 +271,8 @@ static void fchecksize (LoadState *S, size_t size, const char *tname) { #define checksize(S,t) fchecksize(S,sizeof(t),#t) static void checkHeader (LoadState *S) { - /* 1st char already checked */ - checkliteral(S, LUA_SIGNATURE + 1, "not a binary chunk"); + /* skip 1st char (already read and checked) */ + checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk"); if (LoadInt(S) != LUAC_VERSION) error(S, "version mismatch"); if (LoadByte(S) != LUAC_FORMAT) diff --git a/manual/manual.of b/manual/manual.of @@ -6408,11 +6408,15 @@ The function starts by looking into the @Lid{package.loaded} table to determine whether @id{modname} is already loaded. If it is, then @id{require} returns the value stored at @T{package.loaded[modname]}. +(The absence of a second result in this case +signals that this call did not have to load the module.) Otherwise, it tries to find a @emph{loader} for the module. To find a loader, -@id{require} is guided by the @Lid{package.searchers} sequence. -By changing this sequence, +@id{require} is guided by the table @Lid{package.searchers}. +Each item in this table is a search function, +that searches for the module in a particular way. +By changing this table, we can change how @id{require} looks for a module. The following explanation is based on the default configuration for @Lid{package.searchers}. @@ -6429,9 +6433,14 @@ it tries an @emph{all-in-one} loader @seeF{package.searchers}. Once a loader is found, @id{require} calls the loader with two arguments: -@id{modname} and an extra value dependent on how it got the loader. -(If the loader came from a file, -this extra value is the file name.) +@id{modname} and an extra value, +a @emph{loader data}, +also returned by the searcher. +The loader data can be any value useful to the module; +for the default searchers, +it indicates where the loader was found. +(For instance, if the loader came from a file, +this extra value is the file path.) If the loader returns any non-nil value, @id{require} assigns the returned value to @T{package.loaded[modname]}. If the loader does not return a non-nil value and @@ -6439,6 +6448,9 @@ has not assigned any value to @T{package.loaded[modname]}, then @id{require} assigns @Rw{true} to this entry. In any case, @id{require} returns the final value of @T{package.loaded[modname]}. +Besides that value, @id{require} also returns as a second result +the loader data returned by the searcher, +which indicates how @id{require} found the module. If there is any error loading or running the module, or if it cannot find any loader for the module, @@ -6558,16 +6570,20 @@ table used by @Lid{require}. @LibEntry{package.searchers| -A table used by @Lid{require} to control how to load modules. +A table used by @Lid{require} to control how to find modules. Each entry in this table is a @def{searcher function}. When looking for a module, @Lid{require} calls each of these searchers in ascending order, with the module name (the argument given to @Lid{require}) as its sole argument. -The function can return another function (the module @def{loader}) -plus an extra value that will be passed to that loader, -or a string explaining why it did not find that module +If the searcher finds the module, +it returns another function, the module @def{loader}, +plus an extra value, a @emph{loader data}, +that will be passed to that loader and +returned as a second result by @Lid{require}. +If it cannot find the module, +it returns a string explaining why (or @nil if it has nothing to say). Lua initializes this table with four searcher functions. @@ -6617,9 +6633,9 @@ into one single library, with each submodule keeping its original open function. All searchers except the first one (preload) return as the extra value -the file name where the module was found, +the file path where the module was found, as returned by @Lid{package.searchpath}. -The first searcher returns no extra value. +The first searcher always returns the string @St{:preload:}. } diff --git a/testes/attrib.lua b/testes/attrib.lua @@ -122,12 +122,13 @@ local oldpath = package.path package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR) -local try = function (p, n, r) +local try = function (p, n, r, ext) NAME = nil - local rr = require(p) + local rr, x = require(p) assert(NAME == n) assert(REQUIRED == p) assert(rr == r) + assert(ext == x) end a = require"names" @@ -143,27 +144,27 @@ assert(package.searchpath("C", package.path) == D"C.lua") assert(require"C" == 25) assert(require"C" == 25) AA = nil -try('B', 'B.lua', true) +try('B', 'B.lua', true, "libs/B.lua") assert(package.loaded.B) assert(require"B" == true) assert(package.loaded.A) assert(require"C" == 25) package.loaded.A = nil -try('B', nil, true) -- should not reload package -try('A', 'A.lua', true) +try('B', nil, true, nil) -- should not reload package +try('A', 'A.lua', true, "libs/A.lua") package.loaded.A = nil os.remove(D'A.lua') AA = {} -try('A', 'A.lc', AA) -- now must find second option +try('A', 'A.lc', AA, "libs/A.lc") -- now must find second option assert(package.searchpath("A", package.path) == D"A.lc") assert(require("A") == AA) AA = false -try('K', 'L', false) -- default option -try('K', 'L', false) -- default option (should reload it) +try('K', 'L', false, "libs/L") -- default option +try('K', 'L', false, "libs/L") -- default option (should reload it) assert(rawget(_G, "_REQUIREDNAME") == nil) AA = "x" -try("X", "XXxX", AA) +try("X", "XXxX", AA, "libs/XXxX") removefiles(files) @@ -183,14 +184,16 @@ files = { createfiles(files, "_ENV = {}\n", "\nreturn _ENV\n") AA = 0 -local m = assert(require"P1") +local m, ext = assert(require"P1") +assert(ext == "libs/P1/init.lua") assert(AA == 0 and m.AA == 10) assert(require"P1" == m) assert(require"P1" == m) assert(package.searchpath("P1.xuxu", package.path) == D"P1/xuxu.lua") -m.xuxu = assert(require"P1.xuxu") +m.xuxu, ext = assert(require"P1.xuxu") assert(AA == 0 and m.xuxu.AA == 20) +assert(ext == "libs/P1/xuxu.lua") assert(require"P1.xuxu" == m.xuxu) assert(require"P1.xuxu" == m.xuxu) assert(require"P1" == m and m.AA == 10) @@ -267,15 +270,17 @@ else -- test C modules with prefixes in names package.cpath = DC"?" - local lib2 = require"lib2-v2" + local lib2, ext = require"lib2-v2" + assert(string.find(ext, "libs/lib2-v2", 1, true)) -- check correct access to global environment and correct -- parameters assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2") assert(lib2.id("x") == "x") -- test C submodules - local fs = require"lib1.sub" + local fs, ext = require"lib1.sub" assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1") + assert(string.find(ext, "libs/lib1", 1, true)) assert(fs.id(45) == 45) end @@ -293,10 +298,10 @@ do return _ENV end - local pl = require"pl" + local pl, ext = require"pl" assert(require"pl" == pl) assert(pl.xuxu(10) == 30) - assert(pl[1] == "pl" and pl[2] == nil) + assert(pl[1] == "pl" and pl[2] == ":preload:" and ext == ":preload:") package = p assert(type(package.path) == "string")