关于lua的一些资料

来源:互联网 发布:c语言头文件包含规则 编辑:程序博客网 时间:2024/05/17 07:41

找了一些lua的资料。留个标记:

转至:http://blog.sina.com.cn/u/48cbc19a010003x4

Lua 5.1.1 源代码阅读笔记:
(推荐在Notepad++中打开并开启C语言模式)
  
去年的《程序员》上半年部分除了第二期之外,其他的都不怎么样;但是今年的却不一样,特别是算法与开源手册这两章,真实极务实的。这一期中,发现了Lua这块圭玉,十分欣喜。下载把玩两天后,感觉语法不怎么样,有点混,不清晰,除此之外,定位什么的都相当精彩!昨天读了读"The implementation of Lua 5.0",感触挺多的,今天决定看看源代码,试炼一下。
一段一段的说吧,先是数据表示的数据结构:(Lua.h 72~82)
#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
以上是Lua的八种结构(虽然上面不止八种):
① Nil(nil空类型,类似于Scheme的‘(),Python的None),
② Boolean(布尔值),
③ Number(数字是C中的Double,和PASCAL中的Real,只在一些特殊的机型上才会被替换为Integer),④String(字符串不是C的ASCIZ,是类PASCAL型),
⑤ Table(就是一个Table,但是可以用任意的类型(除nil)来索引任意的类型,5.0后使用了侯捷先生所谓的无痛哲学,包装了Table和Array)
⑥ Function(当然是函数了,即所谓的First-Class,广泛用于Scheme,ML中,相当的方便)
⑦ UserData(自定义的用户数据结构,有Light和Heavy两种,后者由Lua来分配管理,用GC)
⑧ Thread(线程,CoRoutine)
怎么样,上面几个#define就明白了吧?(Lua的作者们似乎受PASCAL的影响很深,从T前缀可见;另外从命名法可以看出)
接着:(lua.h 120~126)
LUA_API int   (lua_gettop) (lua_State *L);
LUA_API void  (lua_settop) (lua_State *L, int idx);
LUA_API void  (lua_pushvalue) (lua_State *L, int idx);
…………
LUA_API int   (lua_checkstack) (lua_State *L, int sz);
显然,这是基本的Stack的操作,别的不说,我们看看lua_State,这是什么呢?(Lstate.h 100~126)
struct lua_State {
  CommonHeader;
  lu_byte status;
  StkId top;  /* first free slot in the stack */
  StkId base;  /* base of current function */
  …………
  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) */
};
观察一下,应该是虚拟机的Stack部分。当中省略了一部分。在"The implementation of Lua 5.0"中有这么一段关于Lua的数据表示:
typedef struct {
 int t;
 Value v;
} TObject;

typedef union {
 GCObject *gc;
 void *p;
 lua_Number n;
 int b;
} Value;
这是Lua的数据表示,既然用了union那么自然,C在这个方面的类型检查并不是非常的严厉,所以一切的数据都可以装得下,且只用一个统一的指针就好了,不用void *转换。诚然这样极为浪费存储器空间,但是,大家仔细想想,Lua的用途是什么?Lua可以用来配置C/C++程序,这就是它的定位。数学计算,sorry,it's not my business.
继续,对于一个指向内存特定区域的GCObject是什么样的呢?(Lstate.h 135~144)
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 */
};
名字都很清晰,代码真是没的说,对于仅仅需要地址的TString,Udata和Closure用union,对于Table,Proto,UpVal,thread自然需要更多的信息。看看TString:(Lobject.h 199~207)
typedef union TString {
  L_Umaxalign dummy;  /* ensures maximum alignment for strings */
  struct {
    CommonHeader;
    lu_byte reserved;
    unsigned int hash;
    size_t len;
  } tsv;
} TString;
其中L_Umaxalign是
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign ;
而LUAI_USER_ALIGNMENT_T是
#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; }
明白了吗?这里是一个对齐(Alignment),对齐double,void*和long。为什么要写这三个?不是double最长吗?根据不同的机器,不同的编译器的定义是不同的。因此同时用这三者来代表最长的数和最长的指针。这一点可以从注释中看出来,同样,为什么叫“USER”呢,因为这因机器而定,需要USER们来修改。
TString中的dummy自然就是占位符咯。
看看Udata:(Lobject.h 215~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;
Udata这种用户类型也是由Table来完成的,但是从这里暂时还看不出来Udata怎么区分Light和Heavy的。
该代码下放的Proto部分有个TValue其原型:(Lobject.h 73~75)
typedef struct lua_TValue {
  TValuefields;
} TValue;
而TValuefields:(Lobject.h 71)
#define TValuefields Value value; int tt
这就是所谓的TObject二元组(value,tag)。
对于频频出现的CommonHeader,有:(Lobject.h 43)
#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked
有一个问题目前还是不清楚,即dummy要做最大的对齐,但是uv和tsv明显比dummy占用的字节要多,那么怎么对齐呢?
在展示两端赋值语句:(Lobject.h 138~141)
#define setthvalue(L,obj,x) /
 { TValue *i_o=(obj); /
  i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; /
  checkliveness(G(L),i_o); }
以及:(Lobject.h 119~120)
#define setnvalue(obj,x) /
  { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; }
逻辑很清晰,由特定的宏函数来赋值。同样,我们看看Light和Heavy的Udata:
#define setpvalue(obj,x) /
  { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; }

#define setuvalue(L,obj,x) /
  { TValue *i_o=(obj); /
    i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; /
    checkliveness(G(L),i_o); }
向用一个i_o指针指向l-value。
这里,笔者对(x)的结构不太熟悉,因而追踪setuvalue(L,obj,x)的caller:(Lapi.c 1018~1027)
LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
  Udata *u;
  lua_lock(L);
  luaC_checkGC(L);
  u = luaS_newudata(L, size, getcurrenv(L));
  setuvalue(L, L->top, u);      //注:在这里
  api_incr_top(L);
  lua_unlock(L);
  return u + 1;
}
接着回头看lua_State,那个栈类型:(Lstate.h 103)
StkId top;  /* first free slot in the stack */
接着是StkId:(Lobject.h 193)
typedef TValue *StkId;  /* index to stack elements */
原来就是一个指向TValue的指针呀。cast的定义是:(Llimits.h 75)
#define cast(t, exp) ((t)(exp))
接着是checkliveness:(LObject.h 111~113)
#define checkliveness(g,obj) /
  lua_assert(!iscollectable(obj) || /
  ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
其中iscollectable:(LObject.h 189)
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
还有isdead:(Lgc.h 70)
#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS)
另外还有GCheader:(Lobject.h 49~51)
typedef struct GCheader {
  CommonHeader;
} GCheader;
总体说来,这些结构都不难,但是组织得很好。解释一下吧:
setXvalue()中的X是类型的代称。对于非GC类型,直接设置其Value的值及tt的值;对于GC类型,要用cast将该L-Value转换成GCObject *,接着设置tt,再检查是否活跃checkliveness(G(L),i_o)。checkliveness中再断言iscollectable,其gc的gcheader的tt是否与这个obj的类型一样,再者isdead。其中的iscollectable用的方法是观察obj的tt是否大于LUA_TSTRING,因为在那之后的都是可以收集的。

原创粉丝点击