The Concept of Environment
In Lua, objects of type thread, function and userata can be associated with a table called environment. The environment is also a regular table. It can operate like a normal table and store various variables related to the object.
- The environment on the associated thread s can only be accessed through C code.
- The context associated with UserData is meaningless in Lua. This is just to facilitate programmers when they want to associate a table with a userdata.
- The environment associated with function is used to take over access to global variables in this function.
global variable
Global variables in Lua exist in the environment where the current function is placed. Functions in Lua standard library, such as setmetable, string.find, are registered in the environment of the function and can be accessed directly by Lua scripts.
The Environment of Operational Functions
The Lua standard library provides two methods for accessing and manipulating environments associated with functions in Lua code
- Gettfenv Gets the Environment for the Current Function
- setfenv Sets the Context for Current Object Function Association
View all global variables
The current environment is retrieved by getfenv(0) and then traversed to view all global variables.
for k, v in pairs(getfenv(0)) do print ("k: ", k, ", v: ", type(v)) end
Define global and local variables
Variables defined in Lua default to global variables, stored in the environment of the current function. The following code
local env = getfenv(1) print("var: ", env["var"]) print("loval_var: ", env["loval_var"]) var = "Hello, global variable" local local_var = "Hell, local variable" print("after set") print("var: ", env["var"]) print("loval_var: ", env["loval_var"])
The variable VaR is a global variable stored in the current environment and can be accessed by getfenv().var, while the variable local_var is a local variable and the value getfenv().local_var is nil.
results of enforcement
$ lua test3.lua var: nil loval_var: nil after set var: Hello, global variable loval_var: nil
The Environment of Nested Functions
Functions created in threads by load ing and so on are called non-nested functions, and the default environment of non-nested functions is the environment of threads. When you create a function in a function, you set your environment to the default environment for the newly created function.
When accessing global variables, you access the environment of the current function, as shown in the following code
local function f() print("env: ", getfenv()) local function f1() print("env f1: ", getfenv()) end local function f2() print("env f2: ", getfenv()) end f1() f2() end f()
The execution result f, f1, f2 is that the environment of the three functions is the same table.
env: table: 0x25a16b0 env f1: table: 0x25a16b0 env f2: table: 0x25a16b0
Changing the environment of functions
Because functions f1 and f2 are created in function f, the initial value of their environment is the environment of function F. Here's how to change the environment of the function by setfenv
local function f() print("env: ", getfenv()) local function f1() print("env f1: ", getfenv()) end local function f2() print("env f2: ", getfenv()) end setfenv(f2, {}) f1() f2() end f()
The execution results are as follows: getfenv itself is stored in the environment, and the environment of function F2 is set to an empty table by setfenv(f2, {}). When getfenv is called in function f2, script error is caused because getfenv is nil.
env: table: 0x17a66b0 env f1: table: 0x17a66b0 lua: test4.lua:9: attempt to call global 'getfenv' (a nil value) stack traceback: test4.lua:9: in function 'f2' test4.lua:15: in function 'f' test4.lua:18: in main chunk [C]: ?
If the environment of function f is saved by closure, the method can be called in function f2. As shown below
local function f() print("env: ", getfenv()) local function f1() print("env f1: ", getfenv()) end local env = getfenv() local function f2() local e = env e.print("env f2: ", e.getfenv()) end setfenv(f2, {}) f1() f2() end f()
At this point, you can see that the environment of function f2 is different from that of function f and f1.
$ lua test4.lua env: table: 0x1ebf6b0 env f1: table: 0x1ebf6b0 env f2: table: 0x1ec7480
_G
The _G in Lua is a global variable that points to the global environment (thread ed environment). A little more detail can be understood in this way.
- Threads have an environment called a global environment
- In the global environment, there is a variable named _G, whose value is the global environment (_G. _G= _G?).
- Call load(string) or load string in thread, parse Lua code, and generate a function
- The environment of the generated function defaults to thread s
- The use of _G in non-nested functions, such as print(_G), prints the global environment as well as the environment of this function.
Output _G and _G._G, with exactly the same value
$ cat test.lua print(_G) print(_G._G) $ lua test.lua table: 0x1d0c6b0 table: 0x1d0c6b0
If the environment is set by setfenv, the new environment does not have the variable _G.
local function f() local function f1() print("f1: ", _G) end local env = getfenv() local function f2() local e = env e.print("f2: ", _G) end setfenv(f2, {}) f1() f2() end f()
results of enforcement
f1: table: 0xf916b0 f2: nil
The confusion of GETGLOBAL and lua_get Global
Calling print to output the string print("Hello") allows you to view the instructions of the compiled lua virtual machine through luac
$ luac -l -l test.lua main <test.lua:0,0> (4 instructions, 16 bytes at 0xd92530) 0+ params, 2 slots, 0 upvalues, 0 locals, 2 constants, 0 functions 1 [1] GETGLOBAL 0 -1 ; print 2 [1] LOADK 1 -2 ; "Hello" 3 [1] CALL 0 2 1 4 [1] RETURN 0 1 constants (2) for 0xd92530: 1 "print" 2 "Hello" locals (0) for 0xd92530: upvalues (0) for 0xd92530:
Here we focus only on the GETGLOBAL directive. print is a global variable, and we need to get the value of this variable through GETGLOBAL.
For GETGLOBAL, the execution of the Lua virtual machine is as follows (take Lua 5.1 as an example), in the function luaV_execute
case OP_GETGLOBAL: { TValue g; TValue *rb = KBx(i); sethvalue(L, &g, cl->env); lua_assert(ttisstring(rb)); Protect(luaV_gettable(L, &g, rb, ra)); continue; }
Note the sethvalue (L, & amp; g, cl->env); in this sentence, cl represents the current function, cl - > env points to the environment of the current function. That is, the GETGLOBAL instruction obtains the value of the variable from the environment of the current function.
And the function lua_get global does access thread s'global environment directly and get the value of key as name in the global environment.
void lua_getglobal (lua_State *L, const char *name);
The same is "getglobal", but it has two different meanings, which directly confused me for a long time and almost confused me about what is a global variable.