最近在閱讀《Lua的設計與實現》

裡面講到了很多Lua語言的實現細節。我也寫寫文章,以加強印象。

Lua是一種動態型別語言。所以要入手Lua原始碼,從型別定義開始確實是一個不錯的切入點。

C++中的union結構可以在同一塊記憶體當中存取不同型別的值或結構,Lua在型別實現當中大量使用了該結構。

首先在lua。h中我們可以看到Lua對所有基本型別的定義。

Lua中動態型別的實現

Lua中動態型別的實現

除了空型別LUA_TNONE之外的基本型別有:

空型別、布林型、指標、數字、字串、表、函式、UserData、協程

在lobject。h中我們可以看到所有型別的定義。

在Lua中根據資料型別是否需要進行垃圾回收將資料分為了GCObject與其他資料。

在lobject。h中我們可以看到這樣的資料結構

Lua中動態型別的實現

Lua中動態型別的實現

我們可以看到Value型別中有一個GCObject的指標,其實這個物件就包含了所有需要進行垃圾回收的物件。

至於另外幾個我們可以看到

一、*p就指的是輕量級的指標、

二、n的luaNumber透過查詢定義就可以看到其實它就是一個double型別。

Lua中動態型別的實現

Lua中動態型別的實現

Lua中動態型別的實現

Lua中動態型別的實現

在luaconf。h中可以將double改成其他的資料型別以滿足我們的需要。

三、b的int則代表了lua中的整型。

這樣我們就可以透過Value來表示GCObject和所有不需要進行垃圾回收的資料型別。但是GCObject需要繼續細分,我們將會看到GCObject也是一個聯合體。

接下來我們就來看GCObject裡面包含了什麼。

在lstate。h中包含了GCObject的定義。

Lua中動態型別的實現

Lua中動態型別的實現

暫時先不管GCObject中的GCheader到底是什麼,我們就先看下面的幾個資料型別吧。

我們可以看到下面的所有資料型別都是lua的基本型別,包含了字串、UserData、閉包、表等等。這些都是在Lua中需要進行垃圾回收的物件。

透過union我們可以用這種資料結構來表示所有這些型別。

那麼GCheader是什麼呢?

Lua中動態型別的實現

Lua中動態型別的實現

GCHeader其實就包含了一個CommonHeader,那這個CommonHeader是什麼?

其實這指代了垃圾回收物件當中所有需要的資料,包括GCObject指標、資料型別、以及垃圾回收標誌。

在閱讀程式碼的過程中我們也會發現在字串、Table等型別中都是以CommonHeader開頭的。

Lua中動態型別的實現

Lua中動態型別的實現

因為所有的GC物件都以CommonHeader開頭,所以某種程度上,這些所有型別都繼承自CommonHeader。

所以在GCObject中的gch是為了無差別地對待所有的GC物件,也就是所有GC物件的父類(雖然在這裡並沒有面向物件的概念,但是從實現上來說是一樣的)

到現在為止,所有的物件已經能夠被統一的資料結構——Value表示出來了,但是Value並不是自省的,也就是它並不知道自己是什麼型別的,所以最終Lua又最後封裝了一層,

也就是在lobject。h中封裝的TValue:

Lua中動態型別的實現

Lua中動態型別的實現

value代表了值,而tt則是表示其型別,幫助lua識別資料是何種型別。

Lua的通用資料組織結構總結:

上方的資料結構被下方的資料結構所包含。

CommonHead:

//用於表示GC物件的通用資料

GCObject *next//下一個指向的GC物件

lu_byte tt//具體型別

lu_byte marked//GC標誌代表是否需要被回收

GCHeader:

//用於表示GC物件的統一資料結構,便於查詢

CommonHeader

GCObject:

//GC物件聯合體,用於表示任何一個需要進行垃圾回收的物件

GCheader gch//可以表示任何一種GC物件型別

TString ts//字串

UData u//UserData

Closure cl//閉包

Table h//表

Proto p//存放函式原型的資料結構

UpVal uv//上值

lua_State th//虛擬機器/協程

Value:

//GC物件與基本物件聯合體,用於表示所有的lua基本型別

GCObject *gc//GC回收物件

void *p//指標

lua_Number n//浮點數,預設為double

int b//整型

TValue:

//自省的Lua資料型別

Value value//任意基本型別

int tt//型別識別符號

實際在lua內部還需要根據不同型別進行不同操作,雖然在使用lua的時候是不需要知道其型別的,值得型別會隨著賦值而進行變換,但是在內部操作的時候還是需要轉成特定的型別。

畢竟C不是動態型別的。

所以在lobject。h中定義了每種型別設值得型別轉換方法。

Lua中動態型別的實現

Lua中動態型別的實現

內部大量的資料轉換操作讓lua在使用中做到了可以將任意型別塞入到同一個變數當中。

也促成了動態型別語言的優勢。

總的來說Lua的資料結構大體就是這樣。