?
# 探索Lua5.2內部實現:虛擬機指令(4) Table
| name | args | desc |
|---|---|---|
| OP_NEWTABLE | A B C | R(A) := {} (size = B,C) |
NEWTABLE在寄存器A處創建一個table對象。B和C分別用來存儲這個table數組部分和hash部分的初始大小。初始大小是在編譯期計算出來并生成到這個指令中的,目的是使接下來對table的初始化填充不會造成rehash而影響效率。B和C使用“floating point byte”的方法來表示成(eeeeexxx)的二進制形式,其實際值為(1xxx) * 2^(eeeee-1)。
` local?a?=?{};??`
~~~
1???[1]?NEWTABLE????0?0?0??
2???[1]?RETURN??????0?1???
~~~
上面代碼生成一個空的table,放入local變量a,B和C參數都為0。
| name | args | desc |
|---|---|---|
| OP_SETLIST | A B C | R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B |
SETLIST用來配合NEWTABLE,初始化表的數組部分使用的。A為保存待設置表的寄存器,SETLIST要將A下面緊接著的寄存器列表(1--B)中的值逐個設置給表的數組部分。
當表需要初始化數組元素數量比較小的情況下,例如:
` local?a?=?{1,1,1};??`
~~~
????1???[1]?NEWTABLE????0?3?0??
????2???[1]?LOADK???????1?-1????;?1??
????3???[1]?LOADK???????2?-1????;?1??
????4???[1]?LOADK???????3?-1????;?1??
????5???[1]?SETLIST?????0?3?1???;?1??
????6???[1]?RETURN??????0?1??
constants?(1)?for?0x80048eb0:??
????1???1???
~~~
第1行先用NEWTABLE構建一個具有3個數組元素的表,讓到寄存器0中;然后使用3個LOADK向下面3個寄存器裝入常量1;最后使用SETLIST設置表的1~3為寄存器1~寄存器3。
如果需要創建一個很大的表,其中包含很多的數組元素,使用如上方法就會遇到一個問題。將這些指按順序放到寄存器時,會超出寄存器的范圍。解決的辦法就是按照一個固定大小,將這些數組元素分批進行設置。在Lua中,每批的數量由lopcodes.h中的LFIELDS_PER_FLUSH定義,數量為50。所以,大數量的設置會按照50個一批,先將值設置到表下面的寄存器,然后設置給對應的表項。C代表的就是這一次調用SETLIST設置的是第幾批。回到上面的例子,因為只有3個表項,所以1批就搞定了,C的值為1。
下面是一個大表的設置:
~~~
local?a?=???
{??
????1,2,3,4,5,6,7,8,9,0,??
????1,2,3,4,5,6,7,8,9,0,??
????1,2,3,4,5,6,7,8,9,0,??
????1,2,3,4,5,6,7,8,9,0,??
????1,2,3,4,5,6,7,8,9,0,??
????1,2,3??
};??
~~~
~~~
????1???[1]?NEWTABLE????0?30?0??
????2???[3]?LOADK???????1?-1????;?1??
????3???[3]?LOADK???????2?-2????;?2???
...??
????50??[7]?LOADK???????49?-9???;?9??
????51??[7]?LOADK???????50?-10??;?0??
????52??[7]?SETLIST?????0?50?1??;?1??
????53??[8]?LOADK???????1?-1????;?1??
????54??[8]?LOADK???????2?-2????;?2??
????55??[9]?LOADK???????3?-3????;?3??
????56??[9]?SETLIST?????0?3?2???;?2??
????57??[9]?RETURN??????0?1??
constants?(10)?for?0x80048eb0:??
????1???1??
????2???2??
????3???3??
????4???4??
????5???5??
????6???6??
????7???7??
????8???8??
????9???9??
????10??0???
~~~
可以看到,這個表的初始化使用了兩個SETLIST指令。第一個處理前50個,C為1,設置id從(C-1)*50 + 1開始,也就是1。第二個處理余下的3個,C為2,設置的id從(C-1)*50 + 1開始,也就是51。
如果數據非常大,導致需要的批次超出了C的表示范圍,那么C會被設置成0,然后在SETLIST指令后面生成一個EXTRAARG指令,并用其Ax來存儲批次。這與前面說到的LOADKX的處理方法一樣,都是為處理超大數據服務的。
如果使用核能產生多個返回值的表達式(... 和 函數調用)初始化數組項,如果這個初始化不是表構造的最后一項,那么只有第一個返回值會被設置到數組項;如果是最后一項,那么SETLIST中的B會被設置為0,表示從A+1到當前棧頂都用來設置。
SETLIST只負責初始化表的數組部分,對于hash部分,還是通過SETTABLE來初始化。
| name | args | desc |
|---|---|---|
| OP_GETTABLE | A B C | R(A) := R(B)[RK(C)] |
| OP_SETTABLE | A B C | R(A)[RK(B)] := RK(C) |
GETTABLE使用C表示的key,將寄存器B中的表項值獲取到寄存器A中。SETTABLE設置寄存器A的表的B項為C代表的值。
~~~
local?a?=?{};??
a.x?=?1;??
local?b?=?a.x;??
~~~
~~~
1???[1]?NEWTABLE????0?0?0??
2???[2]?SETTABLE????0?-1?-2?;?"x"?1??
3???[3]?GETTABLE????1?0?-1??;?"x" ?
~~~
- 前言
- 探索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)