[lua source code]ADT of TValue

在上一篇里,我们知道一个Lua的对象包含对象的值和对象的类型信息。回顾一下:

typedef struct lua_TValue {
  Value value_;  
  int tt_;
}TValue;

其中,tt_是tag type的简写,一共包含三个部分,分别是:

  • bit位0-3表示大类型
  • bit位4-5表示小类型
  • bit位6表示是否可以垃圾回收

显然,既然Lua的所有类型统一用TValue表示,TValue就应该具备一些基本的操作,用来存取类型示例的基本属性。从Lua的源码来看,可以把TValue的基本操作归结如下:

  1. 判断对象的具体类型
  2. 获取对象的值
  3. 设置对象的值

如果用ADT来表示,可以写成:

TValue:
   get_type()
   get_value()
   set_value()

我们逐个来看。

get type

Lua里,用了一组宏来判断TValue的具体类型,首先是四个萃取tt_信息的宏:

/* raw type tag of a TValue */
#define rttype(o)   ((o)->tt_)

/* tag with no variants (bits 0-3) */
#define novariant(x)    ((x) & 0x0F)

/* type tag of a TValue (bits 0-3 for tags + variant bits 4-5) */
#define ttype(o)    (rttype(o) & 0x3F)

/* type tag of a TValue with no variants (bits 0-3) */
#define ttnov(o)    (novariant(rttype(o)))

其中norariant是直接操作tt_字段的,其他三个参数都是TValue对象,我们可以去掉novariant:

#define rttype(o)   ((o)->tt_)
#define ttype(o)     (rttype(o) & 0x3F)
#define ttnov(o)     (rttype(o) & 0x0F)

进一步,还可以把rttype展开:

#define rttype(o)   ((o)->tt_)
#define ttype(o)     ((o)->tt_ & 0x3F) 
#define ttnov(o)     ((o)->tt_ & 0x0F)

Lua并没有针对tt_三个部分的萃取都单独提供宏,萃取4-5bit以及萃取第6个bit由于后面各只被用到一次,所以不必单独提供一个宏。当然,如果单独提供,则会让api看上去更完备和对称。

进一步,Lua提供了两个基本的check:

#define checktag(o,t)   (rttype(o) == (t))
#define checktype(o,t)   (ttnov(o) == (t))

现在,就可以对每种TValue类型做判断:

/* Macros to test type */
#define ttisnumber(o)         checktype((o), LUA_TNUMBER)
#define ttisfloat(o)           checktag((o), LUA_TNUMFLT)
#define ttisinteger(o)       checktag((o), LUA_TNUMINT)
#define ttisnil(o)           checktag((o), LUA_TNIL)
#define ttisboolean(o)       checktag((o), LUA_TBOOLEAN)
#define ttislightuserdata(o)   checktag((o), LUA_TLIGHTUSERDATA)
#define ttisstring(o)         checktype((o), LUA_TSTRING)
#define ttisshrstring(o)       checktag((o), ctb(LUA_TSHRSTR))
#define ttislngstring(o)       checktag((o), ctb(LUA_TLNGSTR))
#define ttistable(o)           checktag((o), ctb(LUA_TTABLE))
#define ttisfunction(o)     checktype(o, LUA_TFUNCTION)
#define ttisclosure(o)       ((rttype(o) & 0x1F) == LUA_TFUNCTION)
#define ttisCclosure(o)     checktag((o), ctb(LUA_TCCL))
#define ttisLclosure(o)     checktag((o), ctb(LUA_TLCL))
#define ttislcf(o)           checktag((o), LUA_TLCF)
#define ttisfulluserdata(o) checktag((o), ctb(LUA_TUSERDATA))
#define ttisthread(o)         checktag((o), ctb(LUA_TTHREAD))
#define ttisdeadkey(o)       checktag((o), LUA_TDEADKEY)

实际上,我们可以针对上一篇的类型列表做一个缩进:

#define ttisnil(o)               checktag((o), LUA_TNIL)
#define ttislightuserdata(o)     checktag((o), LUA_TLIGHTUSERDATA)
#define ttisboolean(o)           checktag((o), LUA_TBOOLEAN)
#define ttisnumber(o)            checktype((o), LUA_TNUMBER) // 1          
    #define ttisfloat(o)         checktag((o), LUA_TNUMFLT)
    #define ttisinteger(o)       checktag((o), LUA_TNUMINT)
#define ttisfunction(o)          checktype(o, LUA_TFUNCTION) // 2
    #define ttislcf(o)           checktag((o), LUA_TLCF)
    #define ttisclosure(o)       ((rttype(o) & 0x1F) == LUA_TFUNCTION) // **4**
        #define ttisCclosure(o)  checktag((o), ctb(LUA_TCCL))
        #define ttisLclosure(o)  checktag((o), ctb(LUA_TLCL))
#define ttisstring(o)            checktype((o), LUA_TSTRING) // 3
    #define ttisshrstring(o)     checktag((o), ctb(LUA_TSHRSTR))
    #define ttislngstring(o)     checktag((o), ctb(LUA_TLNGSTR))
#define ttisfulluserdata(o)      checktag((o), ctb(LUA_TUSERDATA))
#define ttistable(o)             checktag((o), ctb(LUA_TTABLE))
#define ttisthread(o)            checktag((o), ctb(LUA_TTHREAD))
#define ttisdeadkey(o)           checktag((o), LUA_TDEADKEY)

可以看到,1,2,3三个地方判断大类型,用的是checktype,也就是只需判断0-3bit即可;而4这个地方判断则使用的是4-5bit判断小类型。其余的地方使用的是0-6bit;不过还差一点,那就是cbt宏。展开看一下:

/* Bit mark for collectable types */
#define BIT_ISCOLLECTABLE    (1 << 6)

/* mark a tag as collectable */
#define ctb(t)            ((t) | BIT_ISCOLLECTABLE)

可见,ctb宏的作用是设置6个bit,表示这是一个可垃圾回收类型。此外,针对GCObject,Lua还提供了一个判断TValue里的tag type和GCObject里的tag type是否一致的判断:

/* Macros for internal tests */
#define righttt(obj)        (ttype(obj) == gcvalue(obj)->tt)

get value

获取无类型信息的Value:

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

进一步,根据上一节的类型判断,就可以实现带类型校验的值获取方法,依然是一组宏:

/* Macros to access values */
#define ivalue(o)    check_exp(ttisinteger(o), val_(o).i)
#define fltvalue(o)  check_exp(ttisfloat(o), val_(o).n)
#define nvalue(o)    check_exp(ttisnumber(o), \
   (ttisinteger(o) ? cast_num(ivalue(o)) : fltvalue(o)))
#define gcvalue(o)   check_exp(iscollectable(o), val_(o).gc)
#define pvalue(o)    check_exp(ttislightuserdata(o), val_(o).p)
#define tsvalue(o)   check_exp(ttisstring(o), gco2ts(val_(o).gc))
#define uvalue(o)    check_exp(ttisfulluserdata(o), gco2u(val_(o).gc))
#define clvalue(o)   check_exp(ttisclosure(o), gco2cl(val_(o).gc))
#define clLvalue(o)  check_exp(ttisLclosure(o), gco2lcl(val_(o).gc))
#define clCvalue(o)  check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))
#define fvalue(o)    check_exp(ttislcf(o), val_(o).f)
#define hvalue(o)    check_exp(ttistable(o), gco2t(val_(o).gc))
#define bvalue(o)    check_exp(ttisboolean(o), val_(o).b)
#define thvalue(o)   check_exp(ttisthread(o), gco2th(val_(o).gc))

/* a dead value may get the 'gc' field, but cannot access its contents */
#define deadvalue(o)   check_exp(ttisdeadkey(o), cast(void *, val_(o).gc))

#define l_isfalse(o)   (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))

#define iscollectable(o)   (rttype(o) & BIT_ISCOLLECTABLE)

注意到,里面用到了几个gco2x的宏,这组宏的作用是把GCObject的子类型转为更具体的类型,再次展开:

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

/* macros to convert a GCObject into a specific value */
#define gco2ts(o)  \
    check_exp(novariant((o)->tt) == LUA_TSTRING, &((cast_u(o))->ts))
#define gco2u(o)  check_exp((o)->tt == LUA_TUSERDATA, &((cast_u(o))->u))
#define gco2lcl(o)  check_exp((o)->tt == LUA_TLCL, &((cast_u(o))->cl.l))
#define gco2ccl(o)  check_exp((o)->tt == LUA_TCCL, &((cast_u(o))->cl.c))
#define gco2cl(o)  \
    check_exp(novariant((o)->tt) == LUA_TFUNCTION, &((cast_u(o))->cl))
#define gco2t(o)  check_exp((o)->tt == LUA_TTABLE, &((cast_u(o))->h))
#define gco2p(o)  check_exp((o)->tt == LUA_TPROTO, &((cast_u(o))->p))
#define gco2th(o)  check_exp((o)->tt == LUA_TTHREAD, &((cast_u(o))->th))

出现了一个新的联合体:GCUnion,这个是啥?展开看:

/*
** 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 */
};

哦,原来这个是上一篇的补丁。所有的GCObject都有共同的头部,也就是CommonHeader,所以可以把GCObject转为GCUnion之后,再转为具体类型的指针,安全性由check_exp保证。

反之,又具体类型转为GCObject也应该提供:

/* macro to convert a Lua object into a GCObject */
#define obj2gco(v) \
    check_exp(novariant((v)->tt) < LUA_TDEADKEY, (&(cast_u(v)->gc)))

set value

显然,设置TValue的值,应该同时设置Value和tag type。

/* Macros to set values */
#define settt_(o,t) ((o)->tt_=(t))  // 必须每次赋值都重新设置具体类型

#define setfltvalue(obj,x) \
  { TValue *io=(obj); val_(io).n=(x); settt_(io, LUA_TNUMFLT); }

#define setivalue(obj,x) \
  { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }

#define setnilvalue(obj) settt_(obj, LUA_TNIL)

#define setfvalue(obj,x) \
  { TValue *io=(obj); val_(io).f=(x); settt_(io, LUA_TLCF); }

#define setpvalue(obj,x) \
  { TValue *io=(obj); val_(io).p=(x); settt_(io, LUA_TLIGHTUSERDATA); }

#define setbvalue(obj,x) \
  { TValue *io=(obj); val_(io).b=(x); settt_(io, LUA_TBOOLEAN); }

#define setgcovalue(L,obj,x) \
  { TValue *io = (obj); GCObject *i_g=(x); \
    val_(io).gc = i_g; settt_(io, ctb(i_g->tt)); }

#define setsvalue(L,obj,x) \
  { TValue *io = (obj); TString *x_ = (x); \
    val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \
    checkliveness(G(L),io); }

#define setuvalue(L,obj,x) \
  { TValue *io = (obj); Udata *x_ = (x); \
    val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TUSERDATA)); \
    checkliveness(G(L),io); }

#define setthvalue(L,obj,x) \
  { TValue *io = (obj); lua_State *x_ = (x); \
    val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTHREAD)); \
    checkliveness(G(L),io); }

#define setclLvalue(L,obj,x) \
  { TValue *io = (obj); LClosure *x_ = (x); \
    val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TLCL)); \
    checkliveness(G(L),io); }

#define setclCvalue(L,obj,x) \
  { TValue *io = (obj); CClosure *x_ = (x); \
    val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TCCL)); \
    checkliveness(G(L),io); }

#define sethvalue(L,obj,x) \
  { TValue *io = (obj); Table *x_ = (x); \
    val_(io).gc = obj2gco(x_); settt_(io, ctb(LUA_TTABLE)); \
    checkliveness(G(L),io); }

#define setdeadvalue(obj)   settt_(obj, LUA_TDEADKEY)


#define setobj(L,obj1,obj2) \
    { TValue *io1=(obj1); *io1 = *(obj2); \
      (void)L; checkliveness(G(L),io1); }

这组宏在内部都先重新引用下参数再操作,我想是为了避免宏展开的副作用,到处加小括号,例如:

#define setsvalue(L,obj,x) \
  { TValue *io = (obj); TString *x_ = (x); \
    val_(io).gc = obj2gco(x_); settt_(io, ctb(x_->tt)); \
    checkliveness(G(L),io); }

这里把objx分别先赋值给iox_,再使用,但下面的x_则因为只用了一次就没做这个赋值动作:

#define setivalue(obj,x) \
  { TValue *io=(obj); val_(io).i=(x); settt_(io, LUA_TNUMINT); }

此外,这组宏里面,对GCObject,都要通过obj2gco把具体类型转型后再赋值给val_(io).gc字段。

待续

下一篇希望分析下Lua State对象,也就是所谓的Thread。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容

  • 开篇 介绍了基本运算之后,我们发现还没有正式地去介绍 Lua 的基本类型。今天我们趁机歇息一下,不往下讲新的 AP...
    码上说阅读 1,389评论 0 2
  • 开篇 上一节分析了 lua_arith 的大部分代码,由于篇幅原因,留到本节将继续讲解剩余的部分: 解析 现在我们...
    码上说阅读 810评论 0 1
  • Nginx API for Lua Introduction ngx.arg ngx.var.VARIABLE C...
    吃瓜的东阅读 5,773评论 0 5
  • 版本号:Lua 5.3 Lua Type lua 的类型定义在lobject.h这个文件里,主要的类型如下: no...
    ffl阅读 1,542评论 1 5
  • 岁月像拍在岸上的浪花,美丽过、闪耀过,即使转瞬即逝,仍会偶尔在我们的记忆中闪现。 一篇渺无人烟的花田,兀自美丽着。...
    花悠然阅读 418评论 0 0