Lua 内部目前共存在 10 种数据类型,为了统一表示这些数据类型,Lua 内部实现了一套统一的数据结构,模拟了「基类」的效果。
从顶向下共涉及以下数据结构:
TValue——通用类型数据+类型
TValue
结构可用以表达所有 Lua 中的数据类型,其定义如下:
#define TValuefields Value value; int tt
typedef struct lua_TValue {
TValuefields;
} TValue;
包含了两个字段,分别用以存储特定类型的数据 value
,以及类型信息 tt
。tt
字段有一批关联的宏,用以判断各个具体类型,比如其中两个宏如下:
/* Macros to test type */
#define ttisnil(o) (ttype(o) == LUA_TNIL)
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
// ...
可以看到 tt
的取值有 LUA_TNIL
等,他们的完整列表有:
- #define LUA_TNONE (-1)
- #define LUA_TNIL 0
- #define LUA_TBOOLEAN 1
- #define LUA_TLIGHTUSERDATA 2
- #define LUA_TNUMBER 3
- #define LUA_TSTRING 4
- #define LUA_TTABLE 5
- #define LUA_TFUNCTION 6
- #define LUA_TUSERDATA 7
- #define LUA_TTHREAD 8
这也就是上文所说「10 种」类型的由来。
需要 GC 的各个类型的
CommonHeader
中其实也有一个tt
字段,但为了足以区分布尔、整数等非 GC 类型,在这里还是需要设立一个tt
以区分开来。
Value——通用类型数据
相当于所有数据类型的基类,来看定义:
typedef union {
GCObject *gc;
void *p;
lua_Number n;
int b;
} Value;
Lua 的数据类型有两大类:纳入 GC 管理的,以及不需要 GC 的。他们分别对应着此联合体内的 gc
字段与其他三个字段。注意到有一个宏:
#define ttype(o) ((o)->tt)
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
iscollectable
被用来判断对应数据类型是否需要 GC。也就是说,不需要 GC 的类型共有 TNONE
、TNIL
、TBOOLEAN
、TLIGHTUSERDATA
、TNUMBER
。忽略前两个空值之后,他们刚好一一对应着 Value
联合体内剩下的三个字段:p
即 TLIGHTUSERDATA
,n
即 TNUMBER
,b
即 TBOOLEAN
。
由于 Value
是一个联合体,以上四个字段不会同时使用。但是当使用其中任何一个字段时,都能正确表示对应的类型。(缺点是整个结构体的大小会被最大的部分给撑大,但只要创建数据结构时,不要直接创建 Value
即可,而应该针对具体的数据类型编写具体的创建逻辑)
GCObject——可GC的通用类型数据
union GCObject {
GCheader gch;
union TString ts;
union Udata u;
union Closure cl;
struct Table h;
struct Proto p;
struct UpVal uv;
struct lua_State th; /* thread */
};
首先注意到的是,它仍然是一个联合体,所以仍然可以发挥类似「基类」的作用,表示所有字段对应的 Lua 数据类型。
除了 gch
之外,其他字段都能看出来对应的具体数据类型的影子。以 TString
为例,看看其结构(详细可参考我的这篇文章:Lua 字符串源码解析):
typedef union TString {
L_Umaxalign dummy; /* ensures maximum alignment for strings */
struct {
CommonHeader;
lu_byte reserved;
unsigned int hash;
size_t len;
} tsv;
} TString;
暂且忽略 dummy
字段,第二层首先是一个通用的 CommonHeader
头,其次就是 TString
自己的字段。来看看其定义:
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
值得一提的是,GCObject
的成员之一 GCHeader
,其定义也仅仅包含了 CommonHeader
:
typedef struct GCheader {
CommonHeader;
} GCheader;
除了 GCHeader
之外,其他字段对应的结构体都统一以 CommonHeader
开头。因此,当只需要 GC 信息时,可以借助 gch
字段获取(并忽略各个类型剩余的特异的字段);当需要整个类型的信息时,也可以通过对应的字段获取。
参考资料:
- 《Lua设计与实现》2.2章节
Pingback引用通告: Lua 表源码解析 | chenxy.me
Pingback引用通告: Lua 字符串源码解析 | chenxy.me