为什么在由 load() 编译的函数内部调用的函数的 _ENV 使用 _G 作为其 _ENV 而不是提供给 load() 的 env arg?

问题描述 投票:0回答:2

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
作为这样的论点?

lua environment
2个回答
0
投票

当您从 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() 的环境,而不是从全局环境中获取。


0
投票

当你定义你的

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
的定义。

© www.soinside.com 2019 - 2024. All rights reserved.