在編譯期,如果要訪問變量a時,會依照以下的順序決定變量a的類型:
- a是當前函數的local變量
- a是外層函數的local變量,那麼a是當前函數的upvalue
- a是全局變量
local變量本身就存在於當前的register中,所有的指令都可以直接使用它的id來訪問。而對於upvalue,lua則有專門的指令負責獲取和設置。
全局變量在lua5.1中也是使用專門的指令,而5.2對這一點做了改變。Lua5.2種沒有專門針對全局變量的指令,而是把全局表放到最外層函數的名字為"_ENV"的upvalue中。對於全局變量a,相當於編譯期幫你改成了_ENV.a來進行訪問。
name | args | desc |
---|---|---|
OP_GETUPVAL | ABC | R(A) := UpValue[B] |
OP_SETUPVAL | AB | UpValue[B] := R(A) |
OP_GETTABUP | ABC | R(A) := UpValue[B][RK(C)] |
OP_SETTABUP | ABC | UpValue[A][RK(B)] := RK(C) |
GETUPVAL將B為索引的upvalue的值裝載到A寄存器中。SETUPVAL將A寄存器的值保存到B為索引的upvalue中。
GETTABUP將B為索引的upvalue當作一個table,並將C做為索引的寄存器或者常量當作key獲取的值放入寄存器A。SETTABUP將A為索引的upvalue當作一個table,將C寄存器或者常量的值以B寄存器或常量為key,存入table。
- local u = 0;
- function f()
- local l;
- u = 1;
- l = u;
- g = 1;
- l = g;
- end
- main <test.lua:0,0> (4 instructions at 0x80048eb0)
- 0+ params, 2 slots, 1 upvalue, 1 local, 2 constants, 1 function
- 1 [1] LOADK 0 -1 ; 0
- 2 [8] CLOSURE 1 0 ; 0x80049140
- 3 [2] SETTABUP 0 -2 1 ; _ENV "f"
- 4 [8] RETURN 0 1
- constants (2) for 0x80048eb0:
- 1 0
- 2 "f"
- locals (1) for 0x80048eb0:
- 0 u 2 5
- upvalues (1) for 0x80048eb0:
- 0 _ENV 1 0
- function <test.lua:2,8> (7 instructions at 0x80049140)
- 0 params, 2 slots, 2 upvalues, 1 local, 2 constants, 0 functions
- 1 [3] LOADNIL 0 0
- 2 [4] LOADK 1 -1 ; 1
- 3 [4] SETUPVAL 1 0 ; u
- 4 [5] GETUPVAL 0 0 ; u
- 5 [6] SETTABUP 1 -2 -1 ; _ENV "g" 1
- 6 [7] GETTABUP 0 1 -2 ; _ENV "g"
- 7 [8] RETURN 0 1
- constants (2) for 0x80049140:
- 1 1
- 2 "g"
- locals (1) for 0x80049140:
- 0 l 2 8
- upvalues (2) for 0x80049140:
- 0 u 1 0
- 1 _ENV 0 0
上面的代碼片段生成一個主函數和一個內嵌函數。根據前面說到的變量規則,在內嵌函數中,l是local變量,u是upvalue,g由於既不是local變量,也不是upvalue,當作全局變量處理。我們先來看內嵌函數,生成的指令從17行開始。
第17行的LOADNIL前面已經講過,為local變量賦值。下面的LOADK和SETUPVAL組合,完成了u = 1。因為1是一個常量,存在於常量表中,而lua沒有常量與upvalue的直接操作指令,所以需要先把常量1裝在到臨時寄存器1種,然後將寄存器1的值賦給upvalue 0,也就是u。第20行的GETUPVAL將upvalue u賦給local變量l。第21行開始的SETTABUP和GETTABUP就是前面提到的對全局變量的處理了。g=1被轉化為_ENV.g=1。_ENV是系統預先設置在主函數中的upvalue,所以對於全局變量g的訪問被轉化成對upvalue[_ENV][g]的訪問。SETTABUP將upvalue 1(_ENV代表的upvalue)作為一個table,將常量表2(常量"g")作為key的值設置為常量表1(常量1);GETTABUP則是將upvalue 1作為table,將常量表2為key的值賦給寄存器0(local l)。
No comments:
Post a Comment