Lua 5.4.4
当我从使用
is_env_g()
从字符串编译的 Lua 块内部调用函数 load()
时,为 load()
的 env
参数提供了不同的环境,is_env_g()
函数不使用env
参数作为它的 _ENV
.
遵守以下代码:
-- create a new table with the parent's variables available via the __index metakey
function new_env(parent)
return setmetatable({}, {
__index = parent
})
end
function run_lua_string(luastr, env)
local f = load(luastr, '', 't', env) -- compile luastr using env as the first upvalue (_ENV)
f() -- run the compiled luastr
end
function is_env_g()
print(_ENV == _G)
end
local luastr =
[[
print(_ENV == _G) -- prints 'false' as expected
is_env_g() -- I expect 'false' but receive 'true' - why?
]]
-- prepare a new env; this one "inherits" from _ENV, which in this case is also _G, but it is *not* equal to _G
local env = new_env(_ENV)
-- compile and run luastr in the env we created above
run_lua_string(luastr, env)
当我用
luastr
编译和运行 Lua 代码字符串 run_lua_string
时,print(_ENV == _G)
内的 luastr
行按预期打印 true
。但是,当同一行 (print(_ENV == _G)
) 在另一个函数 is_env_g()
中运行时,它本身是从已编译的 luastr
块中调用的,我收到 false
.
似乎
is_env_g()
函数没有提供给env
的load()
arg作为它的_ENV
上值-这是为什么?
如何修改,以便
is_env_g()
使用提供给 _ENV
的 load()
论点的 env
?我知道我们可以修改它以显式传递luastr
的_ENV
像这样:
function is_env_g(env)
print(env == _G)
end
local luastr =
[[
is_env_g(_ENV)
]]
有没有另一种方法不通过
_ENV
作为这样的论点?
当您从 luastr 中调用 is_env_g() 时,调用它时将 _ENV 设置为全局环境 (_G),而不是您提供给 load().
的环境发生这种情况是因为当 is_env_g() 在 run_lua_string() 之外定义时,它会在定义时从全局环境中捕获其 _ENV。因此,当稍后从 luastr 中调用 is_env_g() 时,它使用定义时的当前环境,即 _G。
要使is_env_g()使用您提供给load()的环境,您可以将其设为run_lua_string()中的本地函数,如下所示:
function run_lua_string(luastr, env)
local function is_env_g()
print(_ENV == _G)
end
local f = load(luastr, '', 't', env) -- compile luastr using env as the first upvalue (_ENV)
f() -- run the compiled luastr
end
通过使 is_env_g() 成为 run_lua_string() 中的局部函数,它将从 env 中捕获其 _ENV,这是您提供给 load() 的环境,而不是从全局环境中获取。
当你定义你的
is_env_g
时,_ENV
被绑定并且值甚至在“字符串块”中仍然存在。由于您在任何地方都没有它,因此将其设置为_G
。参见 2.2 - 环境和全球环境。
debug.getupvalue()
进行检查:
function is_env_g()
print(_ENV == _G)
end
print(debug.getupvalue(is_env_g, 1)) --> _ENV table: 0x5583f6f84c50
如果将它放入“字符串块”,这将向您显示相同的内容。
此外,您可以使用 luac(1) 逐步观察函数中发生的情况:
$ luac -l -l env.lua
...
function <env.lua:13,15> (9 instructions at 0x561c7bb38850)
0 params, 3 slots, 1 upvalue, 0 locals, 2 constants, 0 functions
1 [14] GETTABUP 0 0 0 ; _ENV "print"
2 [14] GETUPVAL 1 0 ; _ENV
3 [14] GETTABUP 2 0 1 ; _ENV "_G"
4 [14] EQ 1 2 1
5 [14] JMP 1 ; to 7
6 [14] LFALSESKIP 1
7 [14] LOADTRUE 1
8 [14] CALL 0 2 1 ; 1 in 0 out
9 [15] RETURN0
constants (2) for 0x561c7bb38850:
0 S "print"
1 S "_G"
locals (0) for 0x561c7bb38850:
upvalues (1) for 0x561c7bb38850:
0 _ENV 0 0
debug.setupvalue
或者用指向相同环境的 is_env_g
包装 _ENV
的定义。