[Lua_Trace] 1. Lua數據結構 - TValue

摘要:[Lua_Trace] 1. Lua數據結構 - TValue

欲深入了解Lua,我認為先從數據結構開始是必要的,

本文將由Lua基礎數據結構(TValue)講起, 進而衍生至TString, Table ⋯ 等。

此源碼分析的版本為 : Lua 5.3.1

 

1. TValue : 基礎數據

Lua中所有的數據結構都由TValue衍伸,以OO的概念來看它就有點像是基底類別(abstract)般的存在著,因此採C語言來模擬,就是定義出一個通用的結構體作為”父類”,然後子類的結構體中以這個父類作為結構體的第一個成員變數。


//lobject.h

typedef struct lua_TValue TValue;

struct lua_TValue {
  TValuefields;
};

/*
** Tagged Values. This is the basic representation of values in Lua,
** an actual value plus a tag with its type.
*/
#define TValuefields	Value value_; int tt_

tt_可再區分為 tt (low byte) & marked (hight byte),前者表示數據的型別(詳細可見下表),後者則是GC回收用途的標記(marked)。

  

value_則是存放各種數據,事實上Lua將數據區分為兩大類:

1. 原始類型 : 透過C語言表示的對應類型,例:void *p表示 light userdata、int 表示 boolean、double 表示 lua_Number‧‧‧等。

                      因此如為原始類型的數據,則根據其tt_型別將數據對應的置於Value union中的 p (void *), b (int), f(lua_CFunction), i (lua_Integer), n(lua_Number)中。 

2. 可被GC回收類型 : 統一使用GCObject的指針表示。


//lobject.h

union Value {
  GCObject *gc;    /* collectable objects */
  void *p;         /* light userdata */
  int b;           /* booleans */
  lua_CFunction f; /* light C functions */
  lua_Integer i;   /* integer numbers */
  lua_Number n;    /* float numbers */
};

GCObject的成員由 GCObject指針*next, 數據類型tt與回收標簽marked組成, 其中tt與marked兩者同TValue中的tt_定義,也是為了識別該數據的型別。

可被GC回收的數據類型都有個共同點就是其結構的第一個成員皆是CommonHeader,此手法同前面提到OO概念,在Lua源碼中算是蠻常見的,因此CommonHeader可算是所有GC回收類型的父類。


/*
** Common type has only the common header
*/
struct GCObject {
  CommonHeader;
};

/*
** Common Header for all collectable objects (in macro form, to be
** included in other objects)
*/
#define CommonHeader	GCObject *next; lu_byte tt; lu_byte marked

 

GCUnion : 專針對GC類型進行轉型的數據結構


//lstate.h

/*
** Union of all collectable objects (only for conversions)
*/
union GCUnion {
  GCObject gc;  /* common header */
  struct TString ts;
  struct Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct lua_State th;  /* thread */
};

一些會被GC回收的型別都集合在GCUnion中  (ex : TString, Udata, Closure, Table, Proto, lua_State)

相對的,GC回收的數據類型中它的第一項元素往往是CommonHeader (GCObject的共有定義,前面也才剛提到)

事實上GCUnion的主用功能是拿來轉型之用,由於在Lua棧上的數據都視為TValue

故Lua運用一些定義(define)協助轉型(ex : hvalue),而這過程中常透過GCUnion來取回數據的真實型別。

例如 :  Table -> TValue -> GCUnion - > Table

上述原Table型別的變數可能因為被存入棧中後被統一視為TValue,

因此如果需要正確的將TValue轉型為Table則可透過 hvalue (如下程式碼) 協助,仔細追朔其中不難發現它最後都會透過GCUnion來達到正確的型別。


//lvm.c

Table *h = hvalue(t);

//lobject.h

#define hvalue(o)	check_exp(ttistable(o), gco2t(val_(o).gc))

#define val_(o)		((o)->value_)

//lstate.h

#define gco2t(o)  check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))

#define cast_u(o)	cast(union GCUnion *, (o))

有任何錯誤請指正,後續我將盡可能的再補充詳細