Procházet zdrojové kódy

'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.
Roberto Ierusalimschy před 6 roky
rodič
revize
ed2872cd3b
4 změnil soubory, kde provedl 66 přidání a 34 odebrání
  1. 17 6
      loadlib.c
  2. 2 2
      lundump.c
  3. 27 11
      manual/manual.of
  4. 20 15
      testes/attrib.lua

+ 17 - 6
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 */
 }
 
 /* }====================================================== */

+ 2 - 2
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)

+ 27 - 11
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:}.
 
 }
 

+ 20 - 15
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")