diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-04-17 14:57:29 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-04-17 14:57:29 -0300 |
commit | ed2872cd3bf6d352f36bbd34529738a60b0b51eb (patch) | |
tree | a4476cca4a43deeb8eaa0d52f0f02b15acc0db09 | |
parent | 2d3f09544895b422eeecf89e0d108da8b8fcdfca (diff) | |
download | lua-ed2872cd3bf6d352f36bbd34529738a60b0b51eb.tar.gz lua-ed2872cd3bf6d352f36bbd34529738a60b0b51eb.tar.bz2 lua-ed2872cd3bf6d352f36bbd34529738a60b0b51eb.zip |
'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.
-rw-r--r-- | loadlib.c | 23 | ||||
-rw-r--r-- | lundump.c | 4 | ||||
-rw-r--r-- | manual/manual.of | 38 | ||||
-rw-r--r-- | testes/attrib.lua | 35 |
4 files changed, 66 insertions, 34 deletions
@@ -576,9 +576,14 @@ static int searcher_Croot (lua_State *L) { | |||
576 | static int searcher_preload (lua_State *L) { | 576 | static int searcher_preload (lua_State *L) { |
577 | const char *name = luaL_checkstring(L, 1); | 577 | const char *name = luaL_checkstring(L, 1); |
578 | lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); | 578 | lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); |
579 | if (lua_getfield(L, -1, name) == LUA_TNIL) /* not found? */ | 579 | if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ |
580 | lua_pushfstring(L, "\n\tno field package.preload['%s']", name); | 580 | lua_pushfstring(L, "\n\tno field package.preload['%s']", name); |
581 | return 1; | 581 | return 1; |
582 | } | ||
583 | else { | ||
584 | lua_pushliteral(L, ":preload:"); | ||
585 | return 2; | ||
586 | } | ||
582 | } | 587 | } |
583 | 588 | ||
584 | 589 | ||
@@ -620,17 +625,23 @@ static int ll_require (lua_State *L) { | |||
620 | /* else must load package */ | 625 | /* else must load package */ |
621 | lua_pop(L, 1); /* remove 'getfield' result */ | 626 | lua_pop(L, 1); /* remove 'getfield' result */ |
622 | findloader(L, name); | 627 | findloader(L, name); |
623 | lua_pushstring(L, name); /* pass name as argument to module loader */ | 628 | lua_rotate(L, -2, 1); /* function <-> loader data */ |
624 | lua_insert(L, -2); /* name is 1st argument (before search data) */ | 629 | lua_pushvalue(L, 1); /* name is 1st argument to module loader */ |
630 | lua_pushvalue(L, -3); /* loader data is 2nd argument */ | ||
631 | /* stack: ...; loader data; loader function; mod. name; loader data */ | ||
625 | lua_call(L, 2, 1); /* run loader to load module */ | 632 | lua_call(L, 2, 1); /* run loader to load module */ |
633 | /* stack: ...; loader data; result from loader */ | ||
626 | if (!lua_isnil(L, -1)) /* non-nil return? */ | 634 | if (!lua_isnil(L, -1)) /* non-nil return? */ |
627 | lua_setfield(L, 2, name); /* LOADED[name] = returned value */ | 635 | lua_setfield(L, 2, name); /* LOADED[name] = returned value */ |
636 | else | ||
637 | lua_pop(L, 1); /* pop nil */ | ||
628 | if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ | 638 | if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ |
629 | lua_pushboolean(L, 1); /* use true as result */ | 639 | lua_pushboolean(L, 1); /* use true as result */ |
630 | lua_pushvalue(L, -1); /* extra copy to be returned */ | 640 | lua_copy(L, -1, -2); /* replace loader result */ |
631 | lua_setfield(L, 2, name); /* LOADED[name] = true */ | 641 | lua_setfield(L, 2, name); /* LOADED[name] = true */ |
632 | } | 642 | } |
633 | return 1; | 643 | lua_rotate(L, -2, 1); /* loader data <-> module result */ |
644 | return 2; /* return module result and loader data */ | ||
634 | } | 645 | } |
635 | 646 | ||
636 | /* }====================================================== */ | 647 | /* }====================================================== */ |
@@ -271,8 +271,8 @@ static void fchecksize (LoadState *S, size_t size, const char *tname) { | |||
271 | #define checksize(S,t) fchecksize(S,sizeof(t),#t) | 271 | #define checksize(S,t) fchecksize(S,sizeof(t),#t) |
272 | 272 | ||
273 | static void checkHeader (LoadState *S) { | 273 | static void checkHeader (LoadState *S) { |
274 | /* 1st char already checked */ | 274 | /* skip 1st char (already read and checked) */ |
275 | checkliteral(S, LUA_SIGNATURE + 1, "not a binary chunk"); | 275 | checkliteral(S, &LUA_SIGNATURE[1], "not a binary chunk"); |
276 | if (LoadInt(S) != LUAC_VERSION) | 276 | if (LoadInt(S) != LUAC_VERSION) |
277 | error(S, "version mismatch"); | 277 | error(S, "version mismatch"); |
278 | if (LoadByte(S) != LUAC_FORMAT) | 278 | if (LoadByte(S) != LUAC_FORMAT) |
diff --git a/manual/manual.of b/manual/manual.of index fea6922e..24ac45ae 100644 --- a/manual/manual.of +++ b/manual/manual.of | |||
@@ -6408,11 +6408,15 @@ The function starts by looking into the @Lid{package.loaded} table | |||
6408 | to determine whether @id{modname} is already loaded. | 6408 | to determine whether @id{modname} is already loaded. |
6409 | If it is, then @id{require} returns the value stored | 6409 | If it is, then @id{require} returns the value stored |
6410 | at @T{package.loaded[modname]}. | 6410 | at @T{package.loaded[modname]}. |
6411 | (The absence of a second result in this case | ||
6412 | signals that this call did not have to load the module.) | ||
6411 | Otherwise, it tries to find a @emph{loader} for the module. | 6413 | Otherwise, it tries to find a @emph{loader} for the module. |
6412 | 6414 | ||
6413 | To find a loader, | 6415 | To find a loader, |
6414 | @id{require} is guided by the @Lid{package.searchers} sequence. | 6416 | @id{require} is guided by the table @Lid{package.searchers}. |
6415 | By changing this sequence, | 6417 | Each item in this table is a search function, |
6418 | that searches for the module in a particular way. | ||
6419 | By changing this table, | ||
6416 | we can change how @id{require} looks for a module. | 6420 | we can change how @id{require} looks for a module. |
6417 | The following explanation is based on the default configuration | 6421 | The following explanation is based on the default configuration |
6418 | for @Lid{package.searchers}. | 6422 | for @Lid{package.searchers}. |
@@ -6429,9 +6433,14 @@ it tries an @emph{all-in-one} loader @seeF{package.searchers}. | |||
6429 | 6433 | ||
6430 | Once a loader is found, | 6434 | Once a loader is found, |
6431 | @id{require} calls the loader with two arguments: | 6435 | @id{require} calls the loader with two arguments: |
6432 | @id{modname} and an extra value dependent on how it got the loader. | 6436 | @id{modname} and an extra value, |
6433 | (If the loader came from a file, | 6437 | a @emph{loader data}, |
6434 | this extra value is the file name.) | 6438 | also returned by the searcher. |
6439 | The loader data can be any value useful to the module; | ||
6440 | for the default searchers, | ||
6441 | it indicates where the loader was found. | ||
6442 | (For instance, if the loader came from a file, | ||
6443 | this extra value is the file path.) | ||
6435 | If the loader returns any non-nil value, | 6444 | If the loader returns any non-nil value, |
6436 | @id{require} assigns the returned value to @T{package.loaded[modname]}. | 6445 | @id{require} assigns the returned value to @T{package.loaded[modname]}. |
6437 | If the loader does not return a non-nil value and | 6446 | 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]}, | |||
6439 | then @id{require} assigns @Rw{true} to this entry. | 6448 | then @id{require} assigns @Rw{true} to this entry. |
6440 | In any case, @id{require} returns the | 6449 | In any case, @id{require} returns the |
6441 | final value of @T{package.loaded[modname]}. | 6450 | final value of @T{package.loaded[modname]}. |
6451 | Besides that value, @id{require} also returns as a second result | ||
6452 | the loader data returned by the searcher, | ||
6453 | which indicates how @id{require} found the module. | ||
6442 | 6454 | ||
6443 | If there is any error loading or running the module, | 6455 | If there is any error loading or running the module, |
6444 | or if it cannot find any loader for the module, | 6456 | or if it cannot find any loader for the module, |
@@ -6558,16 +6570,20 @@ table used by @Lid{require}. | |||
6558 | 6570 | ||
6559 | @LibEntry{package.searchers| | 6571 | @LibEntry{package.searchers| |
6560 | 6572 | ||
6561 | A table used by @Lid{require} to control how to load modules. | 6573 | A table used by @Lid{require} to control how to find modules. |
6562 | 6574 | ||
6563 | Each entry in this table is a @def{searcher function}. | 6575 | Each entry in this table is a @def{searcher function}. |
6564 | When looking for a module, | 6576 | When looking for a module, |
6565 | @Lid{require} calls each of these searchers in ascending order, | 6577 | @Lid{require} calls each of these searchers in ascending order, |
6566 | with the module name (the argument given to @Lid{require}) as its | 6578 | with the module name (the argument given to @Lid{require}) as its |
6567 | sole argument. | 6579 | sole argument. |
6568 | The function can return another function (the module @def{loader}) | 6580 | If the searcher finds the module, |
6569 | plus an extra value that will be passed to that loader, | 6581 | it returns another function, the module @def{loader}, |
6570 | or a string explaining why it did not find that module | 6582 | plus an extra value, a @emph{loader data}, |
6583 | that will be passed to that loader and | ||
6584 | returned as a second result by @Lid{require}. | ||
6585 | If it cannot find the module, | ||
6586 | it returns a string explaining why | ||
6571 | (or @nil if it has nothing to say). | 6587 | (or @nil if it has nothing to say). |
6572 | 6588 | ||
6573 | Lua initializes this table with four searcher functions. | 6589 | Lua initializes this table with four searcher functions. |
@@ -6617,9 +6633,9 @@ into one single library, | |||
6617 | with each submodule keeping its original open function. | 6633 | with each submodule keeping its original open function. |
6618 | 6634 | ||
6619 | All searchers except the first one (preload) return as the extra value | 6635 | All searchers except the first one (preload) return as the extra value |
6620 | the file name where the module was found, | 6636 | the file path where the module was found, |
6621 | as returned by @Lid{package.searchpath}. | 6637 | as returned by @Lid{package.searchpath}. |
6622 | The first searcher returns no extra value. | 6638 | The first searcher always returns the string @St{:preload:}. |
6623 | 6639 | ||
6624 | } | 6640 | } |
6625 | 6641 | ||
diff --git a/testes/attrib.lua b/testes/attrib.lua index dcafd634..4adb42e0 100644 --- a/testes/attrib.lua +++ b/testes/attrib.lua | |||
@@ -122,12 +122,13 @@ local oldpath = package.path | |||
122 | 122 | ||
123 | package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR) | 123 | package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR) |
124 | 124 | ||
125 | local try = function (p, n, r) | 125 | local try = function (p, n, r, ext) |
126 | NAME = nil | 126 | NAME = nil |
127 | local rr = require(p) | 127 | local rr, x = require(p) |
128 | assert(NAME == n) | 128 | assert(NAME == n) |
129 | assert(REQUIRED == p) | 129 | assert(REQUIRED == p) |
130 | assert(rr == r) | 130 | assert(rr == r) |
131 | assert(ext == x) | ||
131 | end | 132 | end |
132 | 133 | ||
133 | a = require"names" | 134 | a = require"names" |
@@ -143,27 +144,27 @@ assert(package.searchpath("C", package.path) == D"C.lua") | |||
143 | assert(require"C" == 25) | 144 | assert(require"C" == 25) |
144 | assert(require"C" == 25) | 145 | assert(require"C" == 25) |
145 | AA = nil | 146 | AA = nil |
146 | try('B', 'B.lua', true) | 147 | try('B', 'B.lua', true, "libs/B.lua") |
147 | assert(package.loaded.B) | 148 | assert(package.loaded.B) |
148 | assert(require"B" == true) | 149 | assert(require"B" == true) |
149 | assert(package.loaded.A) | 150 | assert(package.loaded.A) |
150 | assert(require"C" == 25) | 151 | assert(require"C" == 25) |
151 | package.loaded.A = nil | 152 | package.loaded.A = nil |
152 | try('B', nil, true) -- should not reload package | 153 | try('B', nil, true, nil) -- should not reload package |
153 | try('A', 'A.lua', true) | 154 | try('A', 'A.lua', true, "libs/A.lua") |
154 | package.loaded.A = nil | 155 | package.loaded.A = nil |
155 | os.remove(D'A.lua') | 156 | os.remove(D'A.lua') |
156 | AA = {} | 157 | AA = {} |
157 | try('A', 'A.lc', AA) -- now must find second option | 158 | try('A', 'A.lc', AA, "libs/A.lc") -- now must find second option |
158 | assert(package.searchpath("A", package.path) == D"A.lc") | 159 | assert(package.searchpath("A", package.path) == D"A.lc") |
159 | assert(require("A") == AA) | 160 | assert(require("A") == AA) |
160 | AA = false | 161 | AA = false |
161 | try('K', 'L', false) -- default option | 162 | try('K', 'L', false, "libs/L") -- default option |
162 | try('K', 'L', false) -- default option (should reload it) | 163 | try('K', 'L', false, "libs/L") -- default option (should reload it) |
163 | assert(rawget(_G, "_REQUIREDNAME") == nil) | 164 | assert(rawget(_G, "_REQUIREDNAME") == nil) |
164 | 165 | ||
165 | AA = "x" | 166 | AA = "x" |
166 | try("X", "XXxX", AA) | 167 | try("X", "XXxX", AA, "libs/XXxX") |
167 | 168 | ||
168 | 169 | ||
169 | removefiles(files) | 170 | removefiles(files) |
@@ -183,14 +184,16 @@ files = { | |||
183 | createfiles(files, "_ENV = {}\n", "\nreturn _ENV\n") | 184 | createfiles(files, "_ENV = {}\n", "\nreturn _ENV\n") |
184 | AA = 0 | 185 | AA = 0 |
185 | 186 | ||
186 | local m = assert(require"P1") | 187 | local m, ext = assert(require"P1") |
188 | assert(ext == "libs/P1/init.lua") | ||
187 | assert(AA == 0 and m.AA == 10) | 189 | assert(AA == 0 and m.AA == 10) |
188 | assert(require"P1" == m) | 190 | assert(require"P1" == m) |
189 | assert(require"P1" == m) | 191 | assert(require"P1" == m) |
190 | 192 | ||
191 | assert(package.searchpath("P1.xuxu", package.path) == D"P1/xuxu.lua") | 193 | assert(package.searchpath("P1.xuxu", package.path) == D"P1/xuxu.lua") |
192 | m.xuxu = assert(require"P1.xuxu") | 194 | m.xuxu, ext = assert(require"P1.xuxu") |
193 | assert(AA == 0 and m.xuxu.AA == 20) | 195 | assert(AA == 0 and m.xuxu.AA == 20) |
196 | assert(ext == "libs/P1/xuxu.lua") | ||
194 | assert(require"P1.xuxu" == m.xuxu) | 197 | assert(require"P1.xuxu" == m.xuxu) |
195 | assert(require"P1.xuxu" == m.xuxu) | 198 | assert(require"P1.xuxu" == m.xuxu) |
196 | assert(require"P1" == m and m.AA == 10) | 199 | assert(require"P1" == m and m.AA == 10) |
@@ -267,15 +270,17 @@ else | |||
267 | 270 | ||
268 | -- test C modules with prefixes in names | 271 | -- test C modules with prefixes in names |
269 | package.cpath = DC"?" | 272 | package.cpath = DC"?" |
270 | local lib2 = require"lib2-v2" | 273 | local lib2, ext = require"lib2-v2" |
274 | assert(string.find(ext, "libs/lib2-v2", 1, true)) | ||
271 | -- check correct access to global environment and correct | 275 | -- check correct access to global environment and correct |
272 | -- parameters | 276 | -- parameters |
273 | assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2") | 277 | assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2") |
274 | assert(lib2.id("x") == "x") | 278 | assert(lib2.id("x") == "x") |
275 | 279 | ||
276 | -- test C submodules | 280 | -- test C submodules |
277 | local fs = require"lib1.sub" | 281 | local fs, ext = require"lib1.sub" |
278 | assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1") | 282 | assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1") |
283 | assert(string.find(ext, "libs/lib1", 1, true)) | ||
279 | assert(fs.id(45) == 45) | 284 | assert(fs.id(45) == 45) |
280 | end | 285 | end |
281 | 286 | ||
@@ -293,10 +298,10 @@ do | |||
293 | return _ENV | 298 | return _ENV |
294 | end | 299 | end |
295 | 300 | ||
296 | local pl = require"pl" | 301 | local pl, ext = require"pl" |
297 | assert(require"pl" == pl) | 302 | assert(require"pl" == pl) |
298 | assert(pl.xuxu(10) == 30) | 303 | assert(pl.xuxu(10) == 30) |
299 | assert(pl[1] == "pl" and pl[2] == nil) | 304 | assert(pl[1] == "pl" and pl[2] == ":preload:" and ext == ":preload:") |
300 | 305 | ||
301 | package = p | 306 | package = p |
302 | assert(type(package.path) == "string") | 307 | assert(type(package.path) == "string") |