lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


2015-03-28 6:05 GMT+02:00 Sean Conner <sean@conman.org>:

>   Then perhaps I have the wrong mental image of how upvalues work in Lua,
> because it's doing things I don't expect.
>
>   Okay, explain to me like I'm five how this works.

First, let's explain _ENV.

If the Lua compiler finds a name, it looks for it in the following order:
1. Look for it as a current local, which may be in a containing block.
2. Look for it as an upvalue.
3. Look for it in a table named _ENV, which is looked for in exactly
the same way. The buck stops there because _ENV always exists,
no further away than an upvalue.

The first _ENV of a new Lua state, aka known to varying degrees
of exactness as the distinguished environment, the global table etc,
is stored (by reference, as always when a table is stored) as item
[2] in the registry. Since any red-blooded Lua 5.2+ programmer will
be changing _ENV every so often, another copy of that reference
gets put in _G the moment you open the base library.

You're not really supposed to change either _G or REGISTRY[2]
unless you are absolutely sure of what you are doing. One reason
might be sandboxing: you are about to allow arbitrary Lua code to
be loaded and run, you have nilled out all the routines you do not
want to be available, and you need to close the back door and lock
it.

Now when you change _ENV, in whatever way, the original global
environment is invisible unless you have in some way kept it. E.g.

local _G = _G
local _ENV = myfuncs

The original globals are still available as _G.print etc.

Another way is:

_ENV = setmetatable({print=myprint},{__index=_ENV})

Your old _ENV (which might have been _G) will now still be
seached for names, except `print` which has been redefined.
One of those names should be _G, so simply _ENV=_G works.

Actually, now that I have explained _ENV, I think I've answered
the question.