Excerpt from Lua 5.3 manual:
_G
A global variable (not a function) that holds the global environment (see §2.2). Lua itself does not use this variable; changing its value does not affect any environment, nor vice versa.
Relevant part from §2.2
[…] every chunk is compiled in the scope of an external local variable named
_ENV
, so_ENV
itself is never a free name in a chunk.[…]
Any table used as the value of
_ENV
is called an environment.Lua keeps a distinguished environment called the global environment. This value is kept at a special index in the C registry. In Lua, the global variable
_G
is initialized with this same value. (_G
is never used internally.)When Lua loads a chunk, the default value for its
_ENV
upvalue is the global environment. Therefore, by default, free names in Lua code refer to entries in the global environment
I understand that for every chunk loaded, since _ENV
would be the first upvalue, it is pointed to the global environment table, pointed by _G
by load
.
> =_G, _ENV
table: 006d1bd8 table: 006d1bd8
confirms that both point to the same table. The manual states, rather reassures multiple times, that _ENV
and _G
are just regular names with no hidden meaning and that Lua itself doesn't use it internally. I tried this chunk below:
local a = { }
local b = a -- since tables are objects, both refer to the same table object
print(a, b) -- same address printed twice
a = { } -- point one of them to a newly constructed table
print(a, b) -- new, old table addresses printed
Now doing the same with _G
and _ENV
:
local g = _G -- make an additional reference
print(g, _G, _ENV) -- prints same address thrice
local p = print -- backup print for later use
_ENV = { } -- point _ENV to a new table/environment
p(g, _G, _ENV) -- old, nil, new
table: 00ce1be0 table: 00ce1be0 table: 00ce1be0
table: 00ce1be0 nil table: 00ce96e0
If _G
is an ordinary global, why is it becoming nil
here? If reference counting is done, _G
, was still holding a reference at the time _ENV
released it. Like b
above, it too should be holding on to the old table, no?
However, for the below chunk, _G
is unchanged / preserved!
_ENV = { _G = _G }
_G.print(_G, _ENV, _ENV._G) -- old, new, old
But here it is killed:
_ENV = { g = _G }
_ENV.g.print(_ENV, _ENV.g, _G) -- new, old, nil
Another case where it is preserved:
print(_G, _ENV) -- print same address twice
local newgt = {} -- create new environment
setmetatable(newgt, {__index = _G}) -- set metatable with _G as __index metamethod
_ENV = newgt -- point _ENV to newgt
print(_G, newgt, _ENV) -- old, new, new
With so many variations in the behaviour of _G
, the original reassurance given by the manual seems shaky. What am I missing here?
_ENV.
So, any occurrence of_G
becomes_ENV._G
before executing your script. – Improvisatory