Global Variables and Environment in Lua

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.

  1. Threads have an environment called a global environment
  2. In the global environment, there is a variable named _G, whose value is the global environment (_G. _G= _G?).
  3. Call load(string) or load string in thread, parse Lua code, and generate a function
  4. The environment of the generated function defaults to thread s
  5. 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.

Posted by Andy82 on Wed, 17 Apr 2019 20:39:33 -0700