object

lua 是一种动态类型语言,类型不存在于变量中,而存在于值本身。

语言中定义了 8 种类型的值

  • nil
  • bool
  • number
  • string
  • table
  • function
  • userdata
  • thread

虽然章节名称为 object,和源代码的名称相同。 但是通常都翻译为“对象”,容易与 OOP 中的对象概念混杂在一起。 在本章,更乐意将其译为“值”。

从某种角度而言,程序就是“数据”与“操作数据的方法”。 所以第一步,先来了解 lua 中的值。

tagged value #

章节开始就提到,类型存在于值本身。 在 lua 内部,用 TValue(tagged value)结构表示值的概念。

67
68
69
70
71
72
73
74
75
/*
** Tagged Values
*/

#define TValuefields	Value value; int tt

typedef struct lua_TValue {
  TValuefields;
} TValue;
Code Snippet 1: lobject.h

tt 表示值的类型,value 表示值的数据。 明显地,类型是值的一部分。

type #

在 TValue 中,类型 tt 用 int 来标识,可以在代码中看到所有基础类型的宏定义

69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
** basic types
*/
#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
Code Snippet 2: lua.h

完全对应 lua 中的 8 种类型。

同时定义了相应的宏,方便检测值的类型。

78
79
80
81
82
83
84
85
86
87
88
89
90
/* Macros to test type */
#define ttisnil(o)	(ttype(o) == LUA_TNIL)
#define ttisnumber(o)	(ttype(o) == LUA_TNUMBER)
#define ttisstring(o)	(ttype(o) == LUA_TSTRING)
#define ttistable(o)	(ttype(o) == LUA_TTABLE)
#define ttisfunction(o)	(ttype(o) == LUA_TFUNCTION)
#define ttisboolean(o)	(ttype(o) == LUA_TBOOLEAN)
#define ttisuserdata(o)	(ttype(o) == LUA_TUSERDATA)
#define ttisthread(o)	(ttype(o) == LUA_TTHREAD)
#define ttislightuserdata(o)	(ttype(o) == LUA_TLIGHTUSERDATA)

/* Macros to access values */
#define ttype(o)	((o)->tt)
Code Snippet 3: lobject.h

细心如你,一定发现多出了一种 lightuserdata 类型。 这是由 userdata 细分出来的一种类型,目前先不做细致的解释, 之后到相应章节再具体分析。

value #

TValue 中,数据 value 用 union Value 结构来表示,有效利用内存空间。

56
57
58
59
60
61
62
63
64
/*
** Union of all Lua values
*/
typedef union {
  GCObject *gc;
  void *p;
  lua_Number n;
  int b;
} Value;
Code Snippet 4: lobject.h

不同类型的数据使用不同的键值来存取。

detail #

下面针对不同类型的值,详细分析。

nil #

nil 是最简单的值,表示没有值。 由于只表示一个含义,故不需要 value,只用 tt 记录类型即可。

27
const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL};
Code Snippet 5: lobject.c
363
364
365
#define luaO_nilobject		(&luaO_nilobject_)

LUAI_DATA const TValue luaO_nilobject_;
Code Snippet 6: lobject.h

可以看出,nil 值在内部是一个单例,所有使用 nil 的地方,都通过 luaO_nilobject 来引用。

bool #

和其它语言一样,bool 值记录 true 和 false。

在存储的安排上,使用 tt 记录类型,用 value 中的 int b = 1/0 表示 true/false。

light userdata #

light userdata 表示 c 和 lua 协同时,由 c 一方传入的数据。 lua 内部只负责引用,而不负责其生命周期管理,什么时候应该释放,lua 不清楚也不过问。

所以内部在用 tt 记录类型之后,只用 value 中 void * p 引用即可。

number #

在默认设置下,lua 语言中所有数字都用 double 来表示。

495
496
497
498
499
500
501
502
503
504
505
/*
** {==================================================================
@@ LUA_NUMBER is the type of numbers in Lua.
** CHANGE the following definitions only if you want to build Lua
** with a number type different from double. You may also need to
** change lua_number2int & lua_number2integer.
** ===================================================================
*/

#define LUA_NUMBER_DOUBLE
#define LUA_NUMBER	double
Code Snippet 7: luaconf.h
98
99
/* type of numbers in Lua */
typedef LUA_NUMBER lua_Number;
Code Snippet 8: lua.h

类似的,用 tt 记录类型,用 value 中 lua_Number n 来记录 number 数值。

collectable #

上面几种类型的值,内部表示都相对简单,剩余几种类型的数据就相对复杂一些。

  • string
  • table
  • function
  • userdata
  • thread

有一点是共通的,它们同属于可 gc 的值(iscollectable)。

189
#define iscollectable(o)	(ttype(o) >= LUA_TSTRING)
Code Snippet 9: lobject.h

lua 内建了 gc 机制,其中关键的结构是 GCObject , 用于表示所有 iscollectable 的值。

GCObject 是 union 结构,和 Value 结构类似,内部键值用于存取不同类型的数据。

133
134
135
136
137
138
139
140
141
142
143
144
145
/*
** Union of all collectable objects
*/
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 */
};
Code Snippet 10: lstate.h

如果仔细观察内部内存的安排,会发现这种方式是非常巧妙的。

gch h p uv th 都是 struct,头部的字段都是 CommonHeader。

39
40
41
42
43
44
45
46
47
48
49
50
51
/*
** 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


/*
** Common header in struct form
*/
typedef struct GCheader {
  CommonHeader;
} GCheader;
Code Snippet 11: lobject.h
338
339
340
341
342
343
344
345
346
347
348
typedef struct Table {
  CommonHeader;
  lu_byte flags;  /* 1<<p means tagmethod(p) is not present */
  lu_byte lsizenode;  /* log2 of size of `node' array */
  struct Table *metatable;
  TValue *array;  /* array part */
  Node *node;
  Node *lastfree;  /* any free position is before this position */
  GCObject *gclist;
  int sizearray;  /* size of `array' array */
} Table;
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*
** Function Prototypes
*/
typedef struct Proto {
  CommonHeader;
  TValue *k;  /* constants used by the function */
  Instruction *code;
  struct Proto **p;  /* functions defined inside the function */
  int *lineinfo;  /* map from opcodes to source lines */
  struct LocVar *locvars;  /* information about local variables */
  TString **upvalues;  /* upvalue names */
  TString  *source;
  int sizeupvalues;
  int sizek;  /* size of `k' */
  int sizecode;
  int sizelineinfo;
  int sizep;  /* size of `p' */
  int sizelocvars;
  int linedefined;
  int lastlinedefined;
  GCObject *gclist;
  lu_byte nups;  /* number of upvalues */
  lu_byte numparams;
  lu_byte is_vararg;
  lu_byte maxstacksize;
} Proto;
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
/*
** Upvalues
*/

typedef struct UpVal {
  CommonHeader;
  TValue *v;  /* points to stack or to its own value */
  union {
    TValue value;  /* the value (when closed) */
    struct {  /* double linked list (when open) */
      struct UpVal *prev;
      struct UpVal *next;
    } l;
  } u;
} UpVal;
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
/*
** `per thread' state
*/
struct lua_State {
  CommonHeader;
  lu_byte status;
  StkId top;  /* first free slot in the stack */
  StkId base;  /* base of current function */
  global_State *l_G;
  CallInfo *ci;  /* call info for current function */
  const Instruction *savedpc;  /* `savedpc' of current function */
  StkId stack_last;  /* last free slot in the stack */
  StkId stack;  /* stack base */
  CallInfo *end_ci;  /* points after end of ci array*/
  CallInfo *base_ci;  /* array of CallInfo's */
  int stacksize;
  int size_ci;  /* size of array `base_ci' */
  unsigned short nCcalls;  /* number of nested C calls */
  unsigned short baseCcalls;  /* nested C calls when resuming coroutine */
  lu_byte hookmask;
  lu_byte allowhook;
  int basehookcount;
  int hookcount;
  lua_Hook hook;
  TValue l_gt;  /* table of globals */
  TValue env;  /* temporary place for environments */
  GCObject *openupval;  /* list of open upvalues in this stack */
  GCObject *gclist;
  struct lua_longjmp *errorJmp;  /* current error recover point */
  ptrdiff_t errfunc;  /* current error handling function (stack index) */
};
Code Snippet 12: lstate.h

ts u cl 虽然是 union,但是其中多余的字段是用于对齐的,实质还是 struct。

196
197
198
199
200
201
202
203
204
205
206
207
/*
** String headers for string table
*/
typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
  } tsv;
} TString;
Code Snippet 13: lobject.h
215
216
217
218
219
220
221
222
223
typedef union Udata {
  L_Umaxalign dummy;  /* ensures maximum alignment for `local' udata */
  struct {
    CommonHeader;
    struct Table *metatable;
    struct Table *env;
    size_t len;
  } uv;
} Udata;
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
/*
** Closures
*/

#define ClosureHeader \
	CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
	struct Table *env

typedef struct CClosure {
  ClosureHeader;
  lua_CFunction f;
  TValue upvalue[1];
} CClosure;


typedef struct LClosure {
  ClosureHeader;
  struct Proto *p;
  UpVal *upvals[1];
} LClosure;


typedef union Closure {
  CClosure c;
  LClosure l;
} Closure;

GCObject 将类型重新备份了一份,GCHeader 中的 tt 和 TValue 中的 tt 是相同的。

105
106
107
108
109
/*
** for internal debug only
*/
#define checkconsistency(obj) \
  lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt))
Code Snippet 14: lobject.h

这样的话,GCObject 可以脱离 TValue,使用 GCHeader gch 先来读取 tt,再根据 tt 来使用不同的键值来引用数据。

148
149
150
151
152
153
154
155
156
157
158
159
/* macros to convert a GCObject into a specific value */
#define rawgco2ts(o)	check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
#define gco2ts(o)	(&rawgco2ts(o)->tsv)
#define rawgco2u(o)	check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
#define gco2u(o)	(&rawgco2u(o)->uv)
#define gco2cl(o)	check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
#define gco2h(o)	check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
#define gco2p(o)	check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
#define gco2uv(o)	check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
#define ngcotouv(o) \
	check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv))
#define gco2th(o)	check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
Code Snippet 15: lstate.h

至于不同类型的数据如何记录,在后面会分章节讨论。

internal #

细心如你,一定又发现了,GCObject 中除了 gch,多出了 p uv,是 8 种类型之外的。

事实上,在 thread 之后,新定义了 3 个类型,同属于 iscollectable,只用于内部使用

  • proto
  • upval
  • deadkey
19
20
21
22
23
24
25
26
27
28
29
30
/* tags for values visible from Lua */
#define LAST_TAG	LUA_TTHREAD

#define NUM_TAGS	(LAST_TAG+1)


/*
** Extra tags for non-values
*/
#define LUA_TPROTO	(LAST_TAG+1)
#define LUA_TUPVAL	(LAST_TAG+2)
#define LUA_TDEADKEY	(LAST_TAG+3)
Code Snippet 16: lobject.h

proto 和 upval 就对应 GCObject 中多出的 2 个键值 p uv, 至于 deadkey,到特定章节再讨论。

practice #

文件建议描述
lobject.h仔细阅读这个文件非常关键,除了定义了关键的数据结构之外,还定义了大量的宏辅助数据操作
lstate.h浏览阅读其中定义了和运行时状态相关的数据结构,尽量理解,加深印象
lobject.c可以阅读实现了 lobject.h 中声明的方法,并非核心内容
comments powered by Disqus