# 探索Lua5.2內部實現:虛擬機指令(3) Upvalues & Globals
在編譯期,如果要訪問變量a時,會依照以下的順序決定變量a的類型:
1. a是當前函數的local變量
2. a是外層函數的local變量,那么a是當前函數的upvalue
3. a是全局變量
local變量本身就存在于當前的register中,所有的指令都可以直接使用它的id來訪問。而對于upvalue,lua則有專門的指令負責獲取和設置。
全局變量在lua5.1中也是使用專門的指令,而5.2對這一點做了改變。Lua5.2種沒有專門針對全局變量的指令,而是把全局表放到最外層函數的名字為"_ENV"的upvalue中。對于全局變量a,相當于編譯期幫你改成了_ENV.a來進行訪問。
| name | args | desc |
|---|---|---|
| OP_GETUPVAL | A B C | R(A) := UpValue[B] |
| OP_SETUPVAL | A B | UpValue[B] := R(A) |
| OP_GETTABUP | A B C | R(A) := UpValue[B][RK(C)] |
| OP_SETTABUP | A B C | 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??(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??(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)。
- 前言
- 探索Lua5.2內部實現:TString
- 探索Lua5.2內部實現:虛擬機指令(1) 概述
- 探索Lua5.2內部實現:虛擬機指令(2) MOVE & LOAD
- 探索Lua5.2內部實現:虛擬機指令(3) Upvalues & Globals
- 探索Lua5.2內部實現:虛擬機指令(4) Table
- 探索Lua5.2內部實現:虛擬機指令(7) 關系和邏輯指令
- 探索Lua5.2內部實現:虛擬機指令(8) LOOP
- 探索Lua5.2內部實現:編譯系統(1) 概述
- 探索Lua5.2內部實現:編譯系統(2) 跳轉的處理
- 探索Lua5.2內部實現:編譯系統(3) 表達式
- 探索Lua5.2內部實現:編譯系統(4) 表達式分類
- 探索Lua5.2內部實現:Garbage Collection(1) 原理
- 探索Lua5.2內部實現:Garbage Collection(2)