首先,要搞明白Nil类型和nil值的实现原理,就要搞明白Lua里变量、值、类型三者之间的关系和实现方式,搞明白了以后,Nil不过是其中一种例子而已。
Lua is a dynamically typed language. This means that variables do not have types; only values do. There are no type definitions in the language. All values carry their own type.
All values in Lua are first-class values. This means that all values can be stored in variables, passed as arguments to other functions, and returned as results.
–Lua Reference Manual
也即,Lua里变量没有类型,值才有类型,并且每一个值都携带了他的类型信息。同时,Lua中的所有值都是第一类值,意味着所有值都可以被存储在变量中、作为参数传递给其他函数以及作为返回值。
落实到代码上,具体实现如下:
typedef struct lua_TValue TValue;
struct lua_TValue {
TValuefields;
};
#define TValuefields Value value_; int tt_
“值”在实现中用TValue类型的变量来存储。把以上语句手工展开一下,如下:
struct TValue {
Value value;
int tt_
};
这里value是具体的值(或对具体值的引用),tt_是该变量的类型。也即实现了上文所说的“值才有类型,并且每一个值都携带了他的类型信息。”
通过追踪若干个宏以后,可以得知tt_中存储的是例如LUA_TNIL、LUA_TBOOLEAN 之类的值,为Lua中的8种类型,此外还有一个NUMTAGS,具体作用未知。
其实到这里已经知道Nil类型是如何实现的了,nil值如何实现还有待发掘。不过既然已经挖到了这一层,不妨继续把Value类型也深挖一下:
union Value {
GCObject *gc; /* collectable objects */
void *p; /* light userdata */
int b; /* booleans */
lua_CFunction f; /* light C functions */
numfield /* numbers */
};
为了节省空间,Value是定义成一个联合体的,由于可以被回收的对象,需要包含更多的信息,所以这里用一个*gc指针引用。其他的四种类型就用C基本类型定义了,其中值得注意的是numfield,他是数字类型的实现,以前提到过Lua里数字是用双精度浮点数实现的,如果需要修改的话应该就是在这里下手,numfield定义如下:
#define numfield lua_Number n; /* numbers */
typedef LUA_NUMBER lua_Number;
#define LUA_NUMBER double
其中第一个宏定义的作用我可以理解,但我还不太明白为什么中间要加上第二个宏定义,也许要等以后通读代码后才能找到答案。
GCObject的定义这里就不分析了,等到以后研究垃圾回收机制的时候再深入研究。
扯远了,回到正题,Nil类型如何实现已经找到,应该就是通过给_tt赋值LUA_TNIL来实现,那么nil值如何实现呢,答案在table的getint函数(通过数字索引返回表项的函数)中:
/*
** search function for integers
*/
const TValue *luaH_getint (Table *t, int key) {
/* (1 <= key && key <= t->sizearray) */
if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))
return &t->array[key-1];
else {
lua_Number nk = cast_num(key);
Node *n = hashnum(t, nk);
do { /* check whether `key' is somewhere in the chain */
if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
return gval(n); /* that's it */
else n = gnext(n);
} while (n);
return luaO_nilobject;
}
}
该函数的流程是:首先在table的array部分查找,若不在,则在hash表部分查找,若还不在,则返回luaO_nilobject对象,毫无疑问,这就是nil值。定义如下:
/*
** (address of) a fixed nil value
*/
#define luaO_nilobject (&luaO_nilobject_)
通过注释可以知道,在Lua中,所有nil值实际上都是同一个(fixed)对象——luaO_nilobject_
该对象定义如下:
LUAI_DDEF const TValue luaO_nilobject_ = {NILCONSTANT};
#define NILCONSTANT {NULL}, LUA_TNIL
展开则得:
LUAI_DDEF const TValue luaO_nilobject_ = {
{NULL},
LUA_TNIL
};
也即,一个value为{NULL},tt_为LUA_TNIL的TValue,这也印证了之前关于Nil类型实现方式的猜测。
至此,Nil类型和nil值的实现方法都已经明晰:
Nil类型:tt_ = LUA_TNIL
nil值:为同一个Nil类型的对象luaO_nilobject_
table类型对不存在的表项进行索引时返回nil值的机制也已经明了:
array部分和hash表部分都不存在该索引时则返回&luaO_nilobject_