diff options
| author | chai <chaifix@163.com> | 2020-09-27 20:31:53 +0800 |
|---|---|---|
| committer | chai <chaifix@163.com> | 2020-09-27 20:31:53 +0800 |
| commit | 63cb4fbbb961da133c68865845eaf22d9b876700 (patch) | |
| tree | 42be163db598df2cf1c11d329c3e5843db4faa0c /src/lua51 | |
| parent | 2dfa15a926f06137f2ba6afcce2e3c1d23300100 (diff) | |
*misc
Diffstat (limited to 'src/lua51')
| -rw-r--r-- | src/lua51/lapi.c | 2 | ||||
| -rw-r--r-- | src/lua51/lcode.c | 17 | ||||
| -rw-r--r-- | src/lua51/ldebug.h | 1 | ||||
| -rw-r--r-- | src/lua51/lfunc.c | 20 | ||||
| -rw-r--r-- | src/lua51/lgc.c | 286 | ||||
| -rw-r--r-- | src/lua51/lgc.h | 44 | ||||
| -rw-r--r-- | src/lua51/llex.h | 2 | ||||
| -rw-r--r-- | src/lua51/lobject.h | 78 | ||||
| -rw-r--r-- | src/lua51/lopcodes.c | 1 | ||||
| -rw-r--r-- | src/lua51/lopcodes.h | 23 | ||||
| -rw-r--r-- | src/lua51/lparser.c | 59 | ||||
| -rw-r--r-- | src/lua51/lparser.h | 14 | ||||
| -rw-r--r-- | src/lua51/lstate.h | 57 | ||||
| -rw-r--r-- | src/lua51/lstring.c | 4 | ||||
| -rw-r--r-- | src/lua51/ltable.c | 4 | ||||
| -rw-r--r-- | src/lua51/lua.c | 6 | ||||
| -rw-r--r-- | src/lua51/lundump.c | 2 | ||||
| -rw-r--r-- | src/lua51/lvm.c | 8 |
18 files changed, 408 insertions, 220 deletions
diff --git a/src/lua51/lapi.c b/src/lua51/lapi.c index a476634..b640e66 100644 --- a/src/lua51/lapi.c +++ b/src/lua51/lapi.c @@ -918,7 +918,7 @@ LUA_API int lua_gc (lua_State *L, int what, int data) { break; } case LUA_GCCOLLECT: { - luaC_fullgc(L); + luaC_fullgc(L); // 一次完成全部GC过程,而不是增量GC break; } case LUA_GCCOUNT: { diff --git a/src/lua51/lcode.c b/src/lua51/lcode.c index 8f25b06..640f107 100644 --- a/src/lua51/lcode.c +++ b/src/lua51/lcode.c @@ -56,11 +56,12 @@ void luaK_nil (FuncState *fs, int from, int n) { } +//c 生成jump指令 int luaK_jump (FuncState *fs) { int jpc = fs->jpc; /* save list of jumps to here */ int j; fs->jpc = NO_JUMP; - j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); + j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); // 生成OP_JUMP指令 luaK_concat(fs, &j, jpc); /* keep them on hold */ return j; } @@ -196,6 +197,7 @@ void luaK_concat (FuncState *fs, int *l1, int l2) { } +//c void luaK_checkstack (FuncState *fs, int n) { int newstack = fs->freereg + n; if (newstack > fs->f->maxstacksize) { @@ -344,7 +346,7 @@ static int code_label (FuncState *fs, int A, int b, int jump) { return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); } - +//c! 根据不同的表达式类型,比如NIL,TRUE,NUMBER等生成对应的字节码 static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { @@ -360,7 +362,7 @@ static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); break; } - case VKNUM: { + case VKNUM: { // 将常量设置到寄存器 luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval)); break; } @@ -416,10 +418,11 @@ static void exp2reg (FuncState *fs, expdesc *e, int reg) { } +//c! 根据expdesc结构生成字节码 void luaK_exp2nextreg (FuncState *fs, expdesc *e) { - luaK_dischargevars(fs, e); - freeexp(fs, e); - luaK_reserveregs(fs, 1); + luaK_dischargevars(fs, e); // 根据变量的作用域来决定这个变量是否需要重定向,即VNONRELOC或VRELOC + freeexp(fs, e); // 如果不需要重定向,释放expdesc寄存器 + luaK_reserveregs(fs, 1); //申请一个寄存器 exp2reg(fs, e, fs->freereg - 1); } @@ -791,7 +794,7 @@ void luaK_fixline (FuncState *fs, int line) { } -//c 指令生成 +//c 指令生成的唯一入口 static int luaK_code (FuncState *fs, Instruction i, int line) { Proto *f = fs->f; dischargejpc(fs); /* `pc' will change */ diff --git a/src/lua51/ldebug.h b/src/lua51/ldebug.h index ba28a97..156503e 100644 --- a/src/lua51/ldebug.h +++ b/src/lua51/ldebug.h @@ -9,6 +9,7 @@ #include "lstate.h" +#include "misc.h" #define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) diff --git a/src/lua51/lfunc.c b/src/lua51/lfunc.c index df2c8e3..5835295 100644 --- a/src/lua51/lfunc.c +++ b/src/lua51/lfunc.c @@ -32,6 +32,7 @@ Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) { //c 新建lua closure //c nelems upvalue的个数 +//c 这个方法有两个地方调用,一个是f_parser,在代码编译后生成一个闭包;一个是闭包内嵌方法执行到OP_CLOSURE指令时新建闭包 Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) { Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems))); luaC_link(L, obj2gco(c), LUA_TFUNCTION); //c 加入GC链表 @@ -43,7 +44,7 @@ Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) { } -//c 新建一个closed upvalue +//c 新建一个upvalue,默认是closed UpVal *luaF_newupval (lua_State *L) { UpVal *uv = luaM_new(L, UpVal); luaC_link(L, obj2gco(uv), LUA_TUPVAL); //c 加入GC链表 @@ -53,6 +54,7 @@ UpVal *luaF_newupval (lua_State *L) { } +//c 设置upvalue的数据指针指向stack中某个值level UpVal *luaF_findupval (lua_State *L, StkId level) { global_State *g = G(L); GCObject **pp = &L->openupval; @@ -82,6 +84,7 @@ UpVal *luaF_findupval (lua_State *L, StkId level) { } +// 把uv从global_state->uvhead或者lua_state->openupval中删除 static void unlinkupval (UpVal *uv) { lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ @@ -89,6 +92,7 @@ static void unlinkupval (UpVal *uv) { } +//c 从global_state->uvhead中删除并释放内存 void luaF_freeupval (lua_State *L, UpVal *uv) { if (uv->v != &uv->u.value) /* is it open? */ unlinkupval(uv); /* remove from open list */ @@ -96,19 +100,23 @@ void luaF_freeupval (lua_State *L, UpVal *uv) { } +//c 设置闭包的upvalue void luaF_close (lua_State *L, StkId level) { + log("luaF_close()"); UpVal *uv; global_State *g = G(L); while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) { GCObject *o = obj2gco(uv); - lua_assert(!isblack(o) && uv->v != &uv->u.value); + lua_assert(!isblack(o) && uv->v != &uv->u.value); //必须是 open upvalue L->openupval = uv->next; /* remove from `open' list */ - if (isdead(g, o)) + if (isdead(g, o)) luaF_freeupval(L, uv); /* free upvalue */ - else { - unlinkupval(uv); - setobj(L, &uv->u.value, uv->v); + else { + unlinkupval(uv); // 从L->openupval中删除 + + setobj(L, &uv->u.value, uv->v); // 拷贝到upvalue的value字段,upvalue储存值 uv->v = &uv->u.value; /* now current value lives here */ + luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */ } } diff --git a/src/lua51/lgc.c b/src/lua51/lgc.c index ae845e9..afebe04 100644 --- a/src/lua51/lgc.c +++ b/src/lua51/lgc.c @@ -48,15 +48,18 @@ #define VALUEWEAK bitmask(VALUEWEAKBIT) -// 标记对象,有点类似一个安全声明,mark了的对象不会被回收,会被标记为灰色(大多数情况下)或黑色 +// markvalue和markobject不同之处在于类型不同,一个接受TValue*类型,一个接受任意合理的指针类型 + +// 标记值为灰色 #define markvalue(g,o) { checkconsistency(o); \ if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } +// 标记对象为灰色 #define markobject(g,t) { if (iswhite(obj2gco(t))) \ reallymarkobject(g, obj2gco(t)); } -// 手动GC +// 设置GC阈值 // estimate 使用中的内存大小估值 // gcpause 控制两次GC间隔的百分数 #define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) @@ -69,37 +72,52 @@ static void removeentry (Node *n) { } -//c 标记object(不标记这个object引用的object,udata除外,需要标记udata的mt和env) -//c 至于标记object引用的object留在扫描阶段 +//c 扫描标记object为灰色(不标记这个object引用的object,udata除外,需要标记udata的mt和env) +//c 至于标记object引用的object留在扫描阶段,这样做是为了缩短时间 static void reallymarkobject (global_State *g, GCObject *o) { lua_assert(iswhite(o) && !isdead(g, o)); - //改为灰色 + + //标记为灰色 white2gray(o); - //对于大部分类型,都是把gcobject加到gray链表 + switch (o->gch.tt) { case LUA_TSTRING: { // 字符串不会引用其他数据,所以略过,不用加到gray list return; } + case LUA_TUSERDATA: { - // udata本身也不会引用其他对象,所以不需要扫描,直接标记为黑色 + // udata本身也不会引用其他对象(除了元表和环境表),所以不需要扫描,直接标记为黑色 Table *mt = gco2u(o)->metatable; // 直接标记为黑色 gray2black(o); /* udata are never gray */ - // 标记一下它的mt和env表 + // 顺便标记一下它的元表和env表,userdata只会引用元表和环境表 if (mt) markobject(g, mt); markobject(g, gco2u(o)->env); return; } + case LUA_TUPVAL: { - UpVal *uv = gco2uv(o); + log("reallymarkobject().LUA_TUPVAL"); + UpVal *uv = gco2uv(o); + // 把upvalue引用的对象标记为灰色 markvalue(g, uv->v); - // 当这个upvalue是closed状态,表示这个uv已经没有与其他数据的引用关系 - // 直接标记黑色 - if (uv->v == &uv->u.value) /* closed? */ - gray2black(o); /* open upvalues are never black */ + // 如果这个upvalue是closed状态,表示这个uv已经没有与其他数据的引用关系 + // 直接把upvalue标记为黑色,upvalue自己保存这个变量 + if (uv->v == &uv->u.value) /* closed? */ + { + log("closed upvalue"); + gray2black(o); /* open upvalues are never black */ + } + // open的upvalue永远不会是黑色的(因为没有意义),需要持续检查,在remarkupvals()函数 + // open upvalue不在gray链中维护,而在global_State的uvhead,等到atomic()的时候再mark + // 所以这里没有把open upvalue放入灰链 + // 参见remarkupvals() return; } + + // 对于 function, table, thread, proto, 将它们加入gray链 + case LUA_TFUNCTION: { gco2cl(o)->c.gclist = g->gray; g->gray = o; @@ -124,29 +142,35 @@ static void reallymarkobject (global_State *g, GCObject *o) { } } - +// 将g->tmudata链中的udata标记为灰色 static void marktmu (global_State *g) { GCObject *u = g->tmudata; if (u) { do { u = u->gch.next; + + // 将这个udata标记为灰色 makewhite(g, u); /* may be marked, if left from previous GC */ reallymarkobject(g, u); + } while (u != g->tmudata); } } -//c 处理 userdata,userdata都在atomic里面处理,而不是propagatemark -//c userdata都跟在mainthread之后(这是一个hack的做法,统一放在一个地方,方便处理) +//c 将需要调用__gc和不需要的udata分开 +// 处理 userdata,userdata都在atomic里面处理,而不是propagatemark +// userdata都跟在mainthread之后(这是一个hack的做法,统一放在一个地方,方便处理) /* move `dead' udata that need finalization to list `tmudata' */ size_t luaC_separateudata (lua_State *L, int all) { global_State *g = G(L); size_t deadmem = 0; + // udata的链表 GCObject **p = &g->mainthread->next; GCObject *curr; + while ((curr = *p) != NULL) /*遍历*/{ - //udata创建的时候是白色,加载mainthread后面 + //userdata创建的时候是白色,加在mainthread后面 if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) // 如果该udata不需要回收,跳过 p = &curr->gch.next; /* don't bother with them */ else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { // 如果没注册__gc @@ -154,7 +178,7 @@ size_t luaC_separateudata (lua_State *L, int all) { markfinalized(gco2u(curr)); /* don't need finalization */ p = &curr->gch.next; } - else { /* must call its gc method */ + else { /* must call its gc method */ // 加入G(L)->tmudata链 deadmem += sizeudata(gco2u(curr)); // 标记为finalized后,还需要加入G(L)->tmudata链表 markfinalized(gco2u(curr)); @@ -187,7 +211,8 @@ static int traversetable (global_State *g, Table *h) { if (mode && ttisstring(mode)) { /* is there a weak mode? */ weakkey = (strchr(svalue(mode), 'k') != NULL); weakvalue = (strchr(svalue(mode), 'v') != NULL); - if (weakkey || weakvalue) { /* is really weak? */ + if (weakkey || weakvalue) { /* is really weak? */ + // 如果是弱表,加到g->weak链表,在扫描的第二阶段atomic处理 h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ h->marked |= cast_byte((weakkey << KEYWEAKBIT) | (weakvalue << VALUEWEAKBIT)); @@ -196,9 +221,11 @@ static int traversetable (global_State *g, Table *h) { g->weak = obj2gco(h); /* ... so put in the appropriate list */ } } - if (weakkey && weakvalue) return 1; // 如果是弱表,返回1 - // 如果不是弱表,遍历table的散列部分和数组部分所有元素 + if (weakkey && weakvalue) + return 1; // 如果是弱表,返回1,从black置回gray以备atomic重新扫描 + + // 如果不是弱表,遍历table的散列部分和数组部分所有元素并标记,加入灰链 //数组 if (!weakvalue) { @@ -220,6 +247,7 @@ static int traversetable (global_State *g, Table *h) { if (!weakvalue) markvalue(g, gval(n)); } } + return weakkey || weakvalue; } @@ -236,7 +264,7 @@ static void traverseproto (global_State *g, Proto *f) { // 标记常量 for (i=0; i<f->sizek; i++) /* mark literals */ markvalue(g, &f->k[i]); - // 标记upvalue + // 标记upvalue名 for (i=0; i<f->sizeupvalues; i++) { /* mark upvalue names */ if (f->upvalues[i]) stringmark(f->upvalues[i]); @@ -254,11 +282,13 @@ static void traverseproto (global_State *g, Proto *f) { } - +// 遍历闭包,标记env, proto, upvalue为灰色,加入灰链 static void traverseclosure (global_State *g, Closure *cl) { markobject(g, cl->c.env); + if (cl->c.isC) { int i; + // 标记upvalue for (i=0; i<cl->c.nupvalues; i++) /* mark its upvalues */ markvalue(g, &cl->c.upvalue[i]); } @@ -266,6 +296,7 @@ static void traverseclosure (global_State *g, Closure *cl) { int i; lua_assert(cl->l.nupvalues == cl->l.p->nups); markobject(g, cl->l.p); + // 标记upvalue for (i=0; i<cl->l.nupvalues; i++) /* mark its upvalues */ markobject(g, cl->l.upvals[i]); } @@ -304,20 +335,32 @@ static void traversestack (global_State *g, lua_State *l) { } -//c 扫描和标记操作入口 /* ** traverse one gray object, turning it to black. ** Returns `quantity' traversed. */ +//c 将对象从灰->黑并从灰色链删除,表示这个对象以及所引用的对象都已经标记过 +// 只遍历*一个*灰色对象,增量地把对象分布到不同的step中(因为遍历对象的引用是非常慢的) +// 和reallymarkobject的区别在后者不会标记所引用的对象(除了userdata),而这个会调用 +// traverse*()进行标记,采用广度优先 static l_mem propagatemark (global_State *g) { - GCObject *o = g->gray; + GCObject *o = g->gray; // 拿到gray的第一个,只处理这一个 lua_assert(isgray(o)); - gray2black(o);//预先标记为黑色 - //只有table、function、thread、proto会引用其他对象 + + gray2black(o);//预先标记为黑色,对于特殊的四个类型需要递归它引用的对象 + +//下面四个语句将对象o排除出gray链即这个对象已经被标记为了黑色,且被引用对象也遍历过了 +//g->gray = h->gclist; +//g->gray = cl->c.gclist; +//g->gray = th->gclist; +//g->gray = p->gclist; + + //只有 table、function、thread、proto 会引用其他对象,需要递归遍历他们引用的对象 + //刚开始进入GCSpropagate阶段时会遍历lua_State, global table, register table switch (o->gch.tt) { case LUA_TTABLE: { //扫描table,尝试标记数组和哈希部分 Table *h = gco2h(o); - g->gray = h->gclist; + g->gray = h->gclist; // 如果是弱表,回退回灰色状态 if (traversetable(g, h)) /* table is weak? */ black2gray(o); /* keep it gray */ @@ -333,12 +376,13 @@ static l_mem propagatemark (global_State *g) { } case LUA_TTHREAD: { //扫描线程对象, 因为lua_State关联的数据变化频繁,所以从graylist中拿出来放到grayaginlist中 lua_State *th = gco2th(o); + // 将lua_state从gray中删除,加到grayagain里 - g->gray = th->gclist; - th->gclist = g->grayagain; + g->gray = th->gclist; // 从gray链删除 + th->gclist = g->grayagain; // 加入grayagin链 g->grayagain = o; - // 颜色退回为灰色 - black2gray(o); + black2gray(o);// 颜色退回为灰色,便于在后续grayagin处理 + traversestack(g, th); return sizeof(lua_State) + sizeof(TValue) * th->stacksize + sizeof(CallInfo) * th->size_ci; @@ -354,7 +398,7 @@ static l_mem propagatemark (global_State *g) { sizeof(LocVar) * p->sizelocvars + sizeof(TString *) * p->sizeupvalues; } - default: + default: // 其余类型不会引用其他对象 lua_assert(0); return 0; } @@ -416,6 +460,7 @@ static void cleartable (GCObject *l) { } +//c 释放对象内存 static void freeobj (lua_State *L, GCObject *o) { switch (o->gch.tt) { case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; @@ -445,7 +490,9 @@ static void freeobj (lua_State *L, GCObject *o) { //c p刚进来是这个散列桶的第一个字符串 #define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) - +//c 回收 count 个(字符串)链表 +// p是在rootgc链上的位置 +// count 每次回收的对象个数 static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { GCObject *curr; global_State *g = G(L); @@ -456,12 +503,12 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { sweepwholelist(L, &gco2th(curr)->openupval); if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); - makewhite(g, curr); /* make it white (for next cycle) */ - p = &curr->gch.next; + makewhite(g, curr); /* make it white (for next cycle) */ // 重新标记为白色,等待下次GC + p = &curr->gch.next; // } else { /* must erase `curr' */ lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); - *p = curr->gch.next; + *p = curr->gch.next; // 将这个对象从rootgc中删除 if (curr == g->rootgc) /* is the first element of the list? */ g->rootgc = curr->gch.next; /* adjust first */ //c! 回收对象,根据类型调用不同的内存释放方法 @@ -473,12 +520,11 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { //c 检查字符串桶的大小,如果太大了,将空的桶删掉,重新分配桶 -//c static void checkSizes (lua_State *L) { global_State *g = G(L); /* check size of string hash */ - //c 如果桶的总大小是用到的桶位(即字符串数量)的4倍,且是MINSTRTABSIZE的2倍 - //c 给桶瘦身 + // 如果桶的总大小是用到的桶位(即字符串数量)的4倍,且是MINSTRTABSIZE的2倍 + // 给桶瘦身 if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && g->strt.size > MINSTRTABSIZE*2) luaS_resize(L, g->strt.size/2); /* table is too big */ @@ -550,46 +596,53 @@ static void markmt (global_State *g) { } -//c 初始化root -//c 将mainthread, +//c GC初始化,根查找阶段,一步完成 +//c 将mainthread /* mark root set */ static void markroot (lua_State *L) { global_State *g = G(L); - // 清空 + + // 1. 清空链表准备这次GC流程 g->gray = NULL; g->grayagain = NULL; g->weak = NULL; - // 标记mainthread + + // 2. 标记mainthread、全局表、注册表为灰色并加入链表gray + // 从这几个开始在扫描阶段将它们引用的对象依次遍历 markobject(g, g->mainthread); - /* make global table be traversed before main stack */ - // 标记 _G 为灰色 - markvalue(g, gt(g->mainthread)); - // 标记注册表为灰色 + markvalue(g, gt(g->mainthread));/* make global table be traversed before main stack */ markvalue(g, registry(L)); - markmt(g); + markmt(g); /*基本类型的元方法*/ + //GC 切换到扫描阶段 g->gcstate = GCSpropagate; } +//c 扫描open状态的upvalue,之所以在 static void remarkupvals (global_State *g) { UpVal *uv; for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); - if (isgray(obj2gco(uv))) + if (isgray(obj2gco(uv))) // 在reallymarkobject的LUA_TUPVAL处理,对于open的upvalue会标记为灰色,等到这里处理 markvalue(g, uv->v); } } -//c 原子化处理grayagain +//c 原子化一步扫描剩余的部分 +// 1. open状态的upvalue +// 2. weak表内容、当前的lua_State、基本类型的元表 +// 3. grayagin链 +// 4. userdata static void atomic (lua_State *L) { global_State *g = G(L); size_t udsize; /* total size of userdata to be finalized */ - /* remark occasional upvalues of (maybe) dead threads */ - remarkupvals(g); - /* traverse objects cautch by write barrier and by 'remarkupvals' */ - propagateall(g); + + //扫描open状态的upvalue + remarkupvals(g);/* remark occasional upvalues of (maybe) dead threads */ // 这个调用之后gray链上会有新的内容(upvalue引用的对象),所以还需要调用下一个函数 + propagateall(g);/* traverse objects cautch by write barrier and by 'remarkupvals' */ + /* remark weak tables */ g->gray = g->weak; g->weak = NULL; @@ -597,44 +650,61 @@ static void atomic (lua_State *L) { markobject(g, L); /* mark running thread */ markmt(g); /* mark basic metatables (again) */ propagateall(g); + /* remark gray again */ g->gray = g->grayagain; g->grayagain = NULL; propagateall(g); - // 处理udata - udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ - marktmu(g); /* mark `preserved' userdata */ + + // 处理userdata + udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ // 将需要调用__gc和不需要的udata分开 + marktmu(g); /* mark `preserved' userdata */ // 将tmudata链里的udata标记为灰色并加入gray链 udsize += propagateall(g); /* remark, to propagate `preserveness' */ + cleartable(g->weak); /* remove collected objects from weak tables */ - /* flip current white */ - g->currentwhite = cast_byte(otherwhite(g)); + + // 结束atomic + g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ g->sweepstrgc = 0; - // 设置当前清理的位置 - g->sweepgc = &g->rootgc; - // 切换到回收字符串阶段 - g->gcstate = GCSsweepstring; + g->sweepgc = &g->rootgc;// 标记当前回收到的位置在rootgc中的位置 + g->gcstate = GCSsweepstring; // 切换到回收字符串阶段 g->estimate = g->totalbytes - udsize; /* first estimate */ } -//c! 单步GC的入口 +//c! GC的入口,会根据当前GC的状态进不同的流程 static l_mem singlestep (lua_State *L) { global_State *g = G(L); /*lua_checkmemory(L);*/ +/* 分为几大步执行 + 1. GCSpause 初始化 + 2. GCSpropagate 扫描 + gray + grayagain + 3. GCSsweepstring 回收字符串 + 4. GCSsweep 回收其他类型 + 5. GCSfinalize 结束 +*/ switch (g->gcstate) { - case GCSpause: { // 初始化 + case GCSpause: { // 初始化,原子操作 markroot(L); /* start a new collection */ return 0; } + case GCSpropagate: { // 扫描并标记 - if (g->gray) // gray list不为空 - return propagatemark(g); + //总的分两步,先按照一定的粒度逐个遍历gray链,然后原子化处理grayagin链表 + if (g->gray) // 如果gray没处理完,处理gray链 + //1.遍历gray链表标记所有数据和引用的数据,有些数据被加入grayagain链表,比如thread + return propagatemark(g); //遍历一个 else { /* no more `gray' objects */ + //2.使用atomic函数遍历grayagin atomic(L); /* finish mark phase */ return 0; } } + case GCSsweepstring: { // 回收字符串 + // 每次回收一个字符串链表 lu_mem old = g->totalbytes; // 回收g->sweepstrgc位置的散列桶 sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); @@ -644,8 +714,10 @@ static l_mem singlestep (lua_State *L) { g->estimate -= old - g->totalbytes; return GCSWEEPCOST; } + case GCSsweep: { // 回收字符串以外的其他类型 lu_mem old = g->totalbytes; + // 回收 GCSWEEPMAX 个对象 g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); if (*g->sweepgc == NULL) { /* nothing more to sweep? */ checkSizes(L); @@ -655,9 +727,11 @@ static l_mem singlestep (lua_State *L) { g->estimate -= old - g->totalbytes; return GCSWEEPMAX*GCSWEEPCOST; } + case GCSfinalize: { // 结束阶段,专门处理 tmudata ,调用userdata的__gc方法,释放native引用 + // 每次处理一个udata if (g->tmudata) { - GCTM(L); + GCTM(L); //调用__gc方法 if (g->estimate > GCFINALIZECOST) g->estimate -= GCFINALIZECOST; return GCFINALIZECOST; @@ -669,6 +743,7 @@ static l_mem singlestep (lua_State *L) { return 0; } } + default: lua_assert(0); return 0; } } @@ -677,7 +752,7 @@ static l_mem singlestep (lua_State *L) { //c! GC 入口 void luaC_step (lua_State *L) { global_State *g = G(L); - l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; + l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; // 本次回收计划回收的内存大小,会影响每次gc执行singlestep的次数 if (lim == 0) lim = (MAX_LUMEM-1)/2; /* no limit */ g->gcdept += g->totalbytes - g->GCthreshold; @@ -699,8 +774,9 @@ void luaC_step (lua_State *L) { } } - +//c STW(stop-the-world) GC,一次性清理GC void luaC_fullgc (lua_State *L) { + log("luaC_fullgc"); global_State *g = G(L); if (g->gcstate <= GCSpropagate) { /* reset sweep marks to sweep all elements (returning them to white) */ @@ -725,62 +801,96 @@ void luaC_fullgc (lua_State *L) { setthreshold(g); } +//屏障是为了应对在“扫描”阶段新建对象时的情况 -//c 向前避障 +//c 向前屏障,指新建的数据从白色->灰色加入灰色链 +// o 黑 v 白 +// 在lgc.h里面定义的几个宏会限定barrier操作不会发生在GCSpause和GCSfinalize阶段,这两个阶段不需要屏蔽操作,正常处理即可 void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); - lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + // GCSfinalize下不会有黑色对象和GCSpause下不会有白色对象 + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); lua_assert(ttype(&o->gch) != LUA_TTABLE); + /* must keep invariant? */ if (g->gcstate == GCSpropagate)// 如果在扫描阶段,把新建的v加入gray链 reallymarkobject(g, v); /* restore invariant */ - else /* don't mind */ // 否则标记为白色,等待下一次GC + else /* don't mind */ // 如果在清除阶段,正常处理,标记为当前白,等待下一次GC makewhite(g, o); /* mark as white just to avoid other barriers */ } -//c 向后避障只作用于 table,将table加入 grayagain 链表 +//c 向后屏障只作用于 table,将table从黑色变灰加入*grayagain*链表(而不是gray链表) +// 指新建数据的引用者从黑色->灰色 +// 之所以table采用向后屏障,而不是向前屏障,将table重新设为灰色并加入*grayagin*在atomic处理 +// 是由于table的1对N特点,避免多次新建项导致的不必要消耗,同时加入grayagin而不是gray,是为了 +// 避免一个对象频繁的“被退回-扫描-回退-扫描”过程,在扫描步骤中一旦这个表被修改了且已经标为了黑 +// 色, 直接把它加到grayagin里处理,而不是像其他类型处理一样使用向前屏障 void luaC_barrierback (lua_State *L, Table *t) { global_State *g = G(L); GCObject *o = obj2gco(t); + lua_assert(isblack(o) && !isdead(g, o)); lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + black2gray(o); /* make table gray (again) */ - // 加到grayagin list里 + + // 加到grayagin链 t->gclist = g->grayagain; g->grayagain = o; } -//c! 将一个新建的GCGamobject(udata除外,加载其他地方,具体看luaS_newudata)加入root,并标记为白色 -//c upvalue 不一定会设为white,所以不会调这个函数 -//c 虽然被加入了rootgc,但是不会被轻易回收,lua有双白色缓冲概念 -//c 分为currentwhite和otherwhite。如果某个对象创建在GC的标记阶段以后,它的white和标记时的white不是一个white, -//c 在回收阶段会判断一下,不会回收这个对象 -//c global_State的currentwhite switch发生在标记阶段后 +//c! luaC_link和luaC_linkupval 函数用于创建对象时调用加入gc链 + + +//c! 将一个新建的GCGamobject:function, table, thread(udata除外,加载其他地方,具体看luaS_newudata) +// 加入rootgc,并标记为白色 +// upvalue 不一定会设为white,所以不会调这个函数 +// 虽然被加入了rootgc,但是不会被轻易回收,lua有双白色缓冲概念 +// 分为currentwhite和otherwhite。如果某个对象创建在GC的标记阶段以后,它的white和标记时的white不是一个white, +// 在回收阶段会判断一下,不会回收这个对象 +// global_State的currentwhite switch发生在标记阶段后 void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { +//log("luaC_link()"); global_State *g = G(L); + + // 将这个新建对象加入rootgc链 o->gch.next = g->rootgc; g->rootgc = o; - o->gch.marked = luaC_white(g);//标记为current white + + o->gch.marked = luaC_white(g);//标记为当前白 o->gch.tt = tt;//设置数据类型 } //c! 将一个upvalue加入root,由于upvalue是对已经存在的对象的间接引用,所以和普通对象不太一样 void luaC_linkupval (lua_State *L, UpVal *uv) { + log("luaC_linkupval()"); + + // 进这个函数里的upvalue都是close upvalue + global_State *g = G(L); - GCObject *o = obj2gco(uv); + GCObject *o = obj2gco(uv); // 这个o是 + + // 将这个upvalue加入rootgc链 o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ g->rootgc = o; + // 这里和普通对象不一样 - if (isgray(o)) { - if (g->gcstate == GCSpropagate) {//如果在扫描阶段,直接将对象置为黑色 - gray2black(o); /* closed upvalues need barrier */ - luaC_barrier(L, uv, uv->v); + if (isgray(o)) { // 在扫描阶段realymarkobject标记为了灰色, + if (g->gcstate == GCSpropagate) {//如果在扫描阶段,直接标记为黑色 + log("GCSpropagate"); + gray2black(o); /* closed upvalues need barrier */ + luaC_barrier(L, uv, uv->v); +/* if(valiswhite(uv->v) && isblack(obj2go(uv))) + luaC_barrierf(L,obj2gco(uv),gcvalue(uv->v)); +*/ } else { /* 否则的话和普通对象一样置为白色 */ /* sweep phase: sweep it (turning it into white) */ makewhite(g, o); + log("not GCSpropagate"); lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); } } diff --git a/src/lua51/lgc.h b/src/lua51/lgc.h index 83007ac..8a23239 100644 --- a/src/lua51/lgc.h +++ b/src/lua51/lgc.h @@ -11,15 +11,18 @@ #include "lobject.h" +#include "misc.h" - +//c! GC的几个阶段 +// 时序上: +// GCSpause > GCSpropagate > GCSsweepstring > GCSsweep > GCSfinalize /* ** Possible states of the Garbage Collector */ -#define GCSpause 0 // 暂停 -#define GCSpropagate 1 // 扫描,正在遍历灰色节点,检查引用情况 +#define GCSpause 0 // 开始 +#define GCSpropagate 1 // 扫描标记,遍历*灰色*节点,检查引用情况 #define GCSsweepstring 2 // 字符串回收阶段 -#define GCSsweep 3 // 除了字符串的其他对象的回收阶段 +#define GCSsweep 3 // 非字符串GC对象的回收 #define GCSfinalize 4 // 终止阶段 @@ -51,19 +54,18 @@ ** bit 5 - object is fixed (should not be collected) ** bit 6 - object is "super" fixed (only the main thread) */ - - -#define WHITE0BIT 0 // 01 -#define WHITE1BIT 1 // 10 -#define BLACKBIT 2 -#define FINALIZEDBIT 3 //标记没有被引用的udata -#define KEYWEAKBIT 3 //标记弱表的key +// 这里不需要一个灰色的bit,非白非黑就是灰色 +#define WHITE0BIT 0 // 01 +#define WHITE1BIT 1 // 10 +#define BLACKBIT 2 // 黑色 +#define FINALIZEDBIT 3 // 标记没有被引用的udata +#define KEYWEAKBIT 3 // 标记弱表的key #define VALUEWEAKBIT 4 // 标记弱表的value -#define FIXEDBIT 5 // 标记lua_state主线程对象不可回收 -#define SFIXEDBIT 6 // 标记lua关键字不要回收 +#define FIXEDBIT 5 // 仅用于标记lua_state主线程对象不可回收 +#define SFIXEDBIT 6 // 标记lua关键字字符串不要回收 #define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) // 结果是11 - +//region 与颜色相关的宏 #define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) #define isblack(x) testbit((x)->gch.marked, BLACKBIT) #define isgray(x) (!isblack(x) && !iswhite(x)) @@ -71,16 +73,15 @@ #define otherwhite(g) (g->currentwhite ^ WHITEBITS) #define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS) -#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) +#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) // 切换white类型 #define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) #define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) -// g->currentwhite & WHITEBITS是 current white -// g->currentwhite ^ WHITEBITS是 otherwhite #define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) +//endregion 与颜色相关的宏 -// 自动触发回收内存,在每次调用内存分配相关API时进行 +//c! 在每次调用内存分配相关API时自动触发回收内存, // 触发条件是当前分配的内存大于阈值GCthreshold // 自动触发不可控,很多人选择关闭它,方法是通过设置一个很大的GCthreshold比如(~0) #define luaC_checkGC(L) { \ @@ -88,8 +89,8 @@ if (G(L)->totalbytes >= G(L)->GCthreshold) \ luaC_step(L); } -//c 避障相关操作,分为向前避障 luaC_barrier luaC_objbarrier 和向后避障 luaC_barriert luaC_objbarriert - +//c 屏障相关操作,分为向前屏障 luaC_barrier luaC_objbarrier 和向后屏障 luaC_barriert luaC_objbarriert +// 向后屏障只针对table类型 #define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ luaC_barrierf(L,obj2gco(p),gcvalue(v)); } @@ -103,6 +104,9 @@ #define luaC_objbarriert(L,t,o) \ { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } + +// API + LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); LUAI_FUNC void luaC_callGCTM (lua_State *L); LUAI_FUNC void luaC_freeall (lua_State *L); diff --git a/src/lua51/llex.h b/src/lua51/llex.h index 8a013f9..101ccdb 100644 --- a/src/lua51/llex.h +++ b/src/lua51/llex.h @@ -30,7 +30,7 @@ enum RESERVED { TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, /* other terminal symbols */ TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, - TK_NAME, TK_STRING, TK_EOS + TK_NAME, TK_STRING, TK_EOS // TK_EOD = end of file }; /* number of reserved words */ diff --git a/src/lua51/lobject.h b/src/lua51/lobject.h index ead9391..5d6df6c 100644 --- a/src/lua51/lobject.h +++ b/src/lua51/lobject.h @@ -43,7 +43,7 @@ typedef union GCObject GCObject; // 需要垃圾回收的类型包含这个头,包含TString, Udata, Proto, UpVal, Closure, Table以及lua_State七个 //c next 指向下一个gc链表的成员 //c tt 数据类型 -//c marked GC标记,用来保存颜色,有白色(2种),灰色和黑色 +//c marked GC标记,用一个字节进行标记,在lgc.h #define CommonHeader GCObject *next; lu_byte tt; lu_byte marked @@ -242,16 +242,16 @@ typedef struct Proto { CommonHeader; //c 常量表,能看出来lua保存常量的单元是函数原型,所有代码片段都会被编译为proto TValue *k; /* constants used by the function */ - //c 函数字节码起始点 + //c 这个函数的字节码 Instruction *code; - //c 内部函数 + //c 这个函数内嵌套的函数 struct Proto **p; /* functions defined inside the function */ int *lineinfo; /* map from opcodes to source lines */ - //c 局部变量 + //c 局部变量,在lparser.c > registerlocalvar注册 struct LocVar *locvars; /* information about local variables */ - //c upvalues + //c upvalue表 TString **upvalues; /* upvalue names */ - TString *source; //文件名 + TString *source; //c 文件名,只有顶层函数有,内嵌函数这个字段是空的 int sizeupvalues; int sizek; /* size of `k' */ int sizecode; @@ -276,7 +276,9 @@ typedef struct Proto { //c 局部变量 typedef struct LocVar { - TString *varname; + //c 变量名,只会在编译器用到,存在FuncState结构,用来查找局部变量ID + TString *varname; + //c int startpc; /* first point where variable is active */ int endpc; /* first point where variable is dead */ } LocVar; @@ -287,19 +289,30 @@ typedef struct LocVar { ** Upvalues */ //c 判断upvalue是关闭的方式是 uv->v == &uv->u.value -//c upvalue有开闭的概念 -//c 开是指upvalue完全由此方法所有,之前的调用已经结束 -//c 关闭是指upvalue还在之前的栈上 +// upvalue有开闭的概念 +// 开是指upvalue完全由此方法所有,之前的调用已经结束 +// 关闭是指upvalue还在之前的栈上 +typedef struct UpVal { + CommonHeader; + TValue *v; /* points to stack or to its own value */ //c upvalue的栈地址 + union { + TValue value; /* the value (when closed) */ + struct { /* double linked list (when open) */ + struct UpVal *prev; + struct UpVal *next; + } l; + } u; +} UpVal; /* -* function func() -* local a = 10; -* local b = function() -* a = a + 1 -* print(a) -* end +* function func() +* local a = 10; +* local b = function() +* a = a + 1 +* print(a) +* end * b() -- 这个是开 -* end -* +* end +* * function func() * local a = 10; * local b = function() @@ -307,22 +320,11 @@ typedef struct LocVar { * print(a) * end * return b -* end -* +* end +* * fn = func() * fn() -- 这个是关 */ -typedef struct UpVal { - CommonHeader; - TValue *v; /* points to stack or to its own value */ //c upvalue的栈地址 - union { - TValue value; /* the value (when closed) */ - struct { /* double linked list (when open) */ - struct UpVal *prev; - struct UpVal *next; - } l; - } u; -} UpVal; /* @@ -335,6 +337,13 @@ typedef struct UpVal { typedef struct CClosure { ClosureHeader; + /* + CommonHeader; //c gc header + lu_byte isC; //c is c closure + lu_byte nupvalues; //c number of upvalues + GCObject *gclist; //c gclist? + struct Table *env //c 这个闭包的环境 + */ lua_CFunction f; TValue upvalue[1]; } CClosure; @@ -342,6 +351,13 @@ typedef struct CClosure { typedef struct LClosure { ClosureHeader; + /* + CommonHeader; //c gc header + lu_byte isC; //c is c closure + lu_byte nupvalues; //c number of upvalues + GCObject *gclist; //c gclist? + struct Table *env //c 这个闭包的环境,在luaF_newLclosure设置 + */ struct Proto *p; // lua闭包的函数原型 UpVal *upvals[1]; // lua闭包的upvalue } LClosure; diff --git a/src/lua51/lopcodes.c b/src/lua51/lopcodes.c index 64652bb..2e1b124 100644 --- a/src/lua51/lopcodes.c +++ b/src/lua51/lopcodes.c @@ -10,7 +10,6 @@ #include "lopcodes.h" - /* ORDER OP */ const char *const luaP_opnames[NUM_OPCODES+1] = { diff --git a/src/lua51/lopcodes.h b/src/lua51/lopcodes.h index 8c0d1a8..f9a9828 100644 --- a/src/lua51/lopcodes.h +++ b/src/lua51/lopcodes.h @@ -27,13 +27,6 @@ unsigned argument. ===========================================================================*/ - -enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ - - -/* -** size and position of opcode arguments. -*/ /* 三种指令的格式 iABC B:9 C:9 A:8 Opcode:6 @@ -42,6 +35,12 @@ iAsBx sBx:18 A:8 Opcode:6 注:sBx是signed BX 寄存器就是相对于callinfo和lua_state的当前调用的base的某个偏移(即ABC值) */ +enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ + + +/* +** size and position of opcode arguments. +*/ //c 单个指令Instruction的每部分的大小 // 9 + 9 + 8 + 6 = 32 bits @@ -87,7 +86,7 @@ iAsBx sBx:18 A:8 Opcode:6 /* ** the following macros help to manipulate instructions */ -//c 获取和设置Instruction中的某个部分 +//c 获取和设置指令中的某个部分 #define GET_OPCODE(i) (cast(OpCode, ((i)>>POS_OP) & MASK1(SIZE_OP,0))) #define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ ((cast(Instruction, o)<<POS_OP)&MASK1(SIZE_OP,POS_OP)))) @@ -212,7 +211,9 @@ OP_FORPREP,/* A sBx R(A)-=R(A+2); pc+=sBx */ OP_TFORLOOP,/* A C R(A+3), ... ,R(A+2+C) := R(A)(R(A+1), R(A+2)); if R(A+3) ~= nil then R(A+2)=R(A+3) else pc++ */ -OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ +OP_SETLIST,/* A B C R(A)[(C-1)*FPF+i] := R(A+i), 1 <= i <= B */ + //setlist有一个优化,当初始化的数量每超过LFIELDS_PER_FLUSH个时,flush一次,这样能够减少 + // 比如初始化时元素个数是3个,那么参数C就是1,只需要flush 1次 OP_CLOSE,/* A close all variables in the stack up to (>=) R(A)*/ OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ @@ -254,7 +255,7 @@ OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ ** bit 6: instruction set register A ** bit 7: operator is a test */ -//c 指令的参数格式 +//c 指令的参数格式,在luaP_opmodes用到 enum OpArgMask { OpArgN, /* argument is not used */ // 未使用(没有座位R()和RK()的参数使用) OpArgU, /* argument is used */ // 使用的 @@ -262,6 +263,8 @@ enum OpArgMask { OpArgK /* argument is a constant or register/constant */ // 寄存器、常量 }; +//c 限制每个指令的具体格式,用来后续判断,起到分类统一处理指令的作用 +//c 即lvm.c 中 RA(i)等宏中的check_exp,不过这个检查是可以跳过的,在llimits.h中定义 LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES]; #define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) diff --git a/src/lua51/lparser.c b/src/lua51/lparser.c index 7da34e7..d868981 100644 --- a/src/lua51/lparser.c +++ b/src/lua51/lparser.c @@ -140,6 +140,7 @@ static void checkname(LexState *ls, expdesc *e) { } +//c 注册一个局部变量,并返回对应的编号ID static int registerlocalvar (LexState *ls, TString *varname) { FuncState *fs = ls->fs; Proto *f = fs->f; @@ -157,13 +158,15 @@ static int registerlocalvar (LexState *ls, TString *varname) { new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) +//c new_localvar() adjustlocalvars() removevars() 用来管理局部变量 + +//c 对于 local a,b,c = 1, 2,3 这样的语句,生成等号坐标对应的变量 static void new_localvar (LexState *ls, TString *name, int n) { FuncState *fs = ls->fs; - luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables"); - fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); + luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables");//最多支持200个局部变量 + fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); // } - static void adjustlocalvars (LexState *ls, int nvars) { FuncState *fs = ls->fs; fs->nactvar = cast_byte(fs->nactvar + nvars); @@ -172,7 +175,6 @@ static void adjustlocalvars (LexState *ls, int nvars) { } } - static void removevars (LexState *ls, int tolevel) { FuncState *fs = ls->fs; while (fs->nactvar > tolevel) @@ -380,22 +382,32 @@ static void close_func (LexState *ls) { } -//c! 编译生成字节码,分析阶段的唯一入口,返回proto指针 +//c!! 编译器入口 +//c! 词法分析>语法分析并生成字节码 +//c 编译生成字节码,分析阶段的唯一入口,返回proto指针 Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { + //c 词法分析、语法分析和代码生成过程中的数据都在这里 struct LexState lexstate; - struct FuncState funcstate; // 分析过程中的临时数据 + struct FuncState funcstate; + lexstate.buff = buff; luaX_setinput(L, &lexstate, z, luaS_new(L, name)); open_func(&lexstate, &funcstate); funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ - luaX_next(&lexstate); /* read first token */ - chunk(&lexstate); + + // lua通过一次遍历就从源代码生成了字节码,为了加快编译 + luaX_next(&lexstate); /* read first token */ + chunk(&lexstate); + + // 代码生成结束进行一些收尾工作 check(&lexstate, TK_EOS); close_func(&lexstate); + lua_assert(funcstate.prev == NULL); lua_assert(funcstate.f->nups == 0); lua_assert(lexstate.fs == NULL); - return funcstate.f; //c 最终生成的字节码 + + return funcstate.f; //c 最终生成的函数原型 } @@ -431,12 +443,12 @@ static void yindex (LexState *ls, expdesc *v) { ** ======================================================================= */ - +// 存放表消息 struct ConsControl { - expdesc v; /* last list item read */ - expdesc *t; /* table descriptor */ - int nh; /* total number of `record' elements */ - int na; /* total number of array elements */ + expdesc v; /* last list item read */ //表在构造过程中最后一个表达式的信息 + expdesc *t; /* table descriptor */ //表构造表达式的信息 + int nh; /* total number of `record' elements */ // 初始化表时,散列部分数据数量 + int na; /* total number of array elements */ // 初始化表时,数组部分数据数量 int tostore; /* number of array elements pending to be stored */ }; @@ -500,12 +512,15 @@ static void constructor (LexState *ls, expdesc *t) { /* constructor -> ?? */ FuncState *fs = ls->fs; int line = ls->linenumber; - int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); // 这条指令会被重定向,A参数会被设为寄存器上某个值 + struct ConsControl cc; cc.na = cc.nh = cc.tostore = 0; cc.t = t; + init_exp(t, VRELOCABLE, pc); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ checknext(ls, '{'); do { @@ -594,12 +609,13 @@ static void body (LexState *ls, expdesc *e, int needself, int line) { } +//c 表达式 static int explist1 (LexState *ls, expdesc *v) { /* explist1 -> expr { `,' expr } */ int n = 1; /* at least one expression */ - expr(ls, v); + expr(ls, v); // 先生成lparser.h > expdesc结构 while (testnext(ls, ',')) { - luaK_exp2nextreg(ls->fs, v); + luaK_exp2nextreg(ls->fs, v); // 根据expdesc结构内容生成对应的字节码 expr(ls, v); n++; } @@ -725,6 +741,7 @@ static void primaryexp (LexState *ls, expdesc *v) { } +//构造表达式 static void simpleexp (LexState *ls, expdesc *v) { /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | FUNCTION body | primaryexp */ @@ -1177,6 +1194,7 @@ static void localfunc (LexState *ls) { } +//c 定义局部变量语句 static void localstat (LexState *ls) { /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ int nvars = 0; @@ -1191,8 +1209,8 @@ static void localstat (LexState *ls) { e.k = VVOID; nexps = 0; } - adjust_assign(ls, nvars, nexps, &e); - adjustlocalvars(ls, nvars); + adjust_assign(ls, nvars, nexps, &e); // 根据等号左右两边的个数调整,如果右边少于左边,则多余的置为nil + adjustlocalvars(ls, nvars); //根据变量数量调整FuncState结构中的nactvar,并调整startpc } @@ -1268,7 +1286,7 @@ static void retstat (LexState *ls) { luaK_ret(fs, first, nret); } - +//c 语法分析,直接生成字节码 static int statement (LexState *ls) { int line = ls->linenumber; /* may be needed for error messages */ switch (ls->t.token) { @@ -1332,6 +1350,7 @@ static void chunk (LexState *ls) { testnext(ls, ';'); lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && ls->fs->freereg >= ls->fs->nactvar); + // 更新寄存器的位置,给本地变量留位置 ls->fs->freereg = ls->fs->nactvar; /* free registers */ } leavelevel(ls); diff --git a/src/lua51/lparser.h b/src/lua51/lparser.h index 18836af..dbd7405 100644 --- a/src/lua51/lparser.h +++ b/src/lua51/lparser.h @@ -34,8 +34,9 @@ typedef enum { VVARARG /* info = instruction pc */ } expkind; +//c 存放表达式信息 typedef struct expdesc { - expkind k; + expkind k; // 表达式类型 union { struct { int info, aux; } s; lua_Number nval; @@ -54,27 +55,32 @@ typedef struct upvaldesc { struct BlockCnt; /* defined in lparser.c */ +//c 编译过程(词法分析、语法分析、代码生成阶段)的临时数据结构 +//c 用来辅助生成字节码 /* state needed to generate code for a given function */ typedef struct FuncState { + //c 函数字节码 Proto *f; /* current function header */ Table *h; /* table to find (and reuse) elements in `k' */ - struct FuncState *prev; /* enclosing function */ + //c 指向父函数的指针 + struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct lua_State *L; /* copy of the Lua state */ struct BlockCnt *bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to `ncode') */ int lasttarget; /* `pc' of last `jump target' */ int jpc; /* list of pending jumps to `pc' */ - int freereg; /* first free register */ + int freereg; /* first free register */ // 用来指示局部变量的栈位置 int nk; /* number of elements in `k' */ int np; /* number of elements in `p' */ short nlocvars; /* number of elements in `locvars' */ lu_byte nactvar; /* number of active local variables */ upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ - unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ + unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ // 在函数原型f->locvars的序号 } FuncState; +//c 编译的唯一入口,包含词法、语法、代码生成 LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name); diff --git a/src/lua51/lstate.h b/src/lua51/lstate.h index 8da0640..cb4244b 100644 --- a/src/lua51/lstate.h +++ b/src/lua51/lstate.h @@ -69,7 +69,6 @@ typedef struct CallInfo { } CallInfo; - #define curr_func(L) (clvalue(L->ci->func)) #define ci_func(ci) (clvalue((ci)->func)) #define f_isLua(ci) (!ci_func(ci)->c.isC) @@ -84,46 +83,61 @@ typedef struct global_State { stringtable strt; /* hash table for strings */ lua_Alloc frealloc; /* function to reallocate memory */ void *ud; /* auxiliary data to `frealloc' */ - lu_byte currentwhite;//当前白 - //c 当前的GC状态,有5个,在lgc.h定义 + +//region 与GC相关 + + lu_byte currentwhite;//当前的白色类型,用于lgc.h>luaC_white() + // 当前的GC状态,有5个,在lgc.h定义 lu_byte gcstate; /* state of garbage collector */ - //c strt中字符串散列桶索引,字符串回收阶段每次回收一个散列桶的字符串, + // strt中字符串散列桶索引,字符串回收阶段每次回收一个散列桶的字符串,记录对应的散列桶索引 int sweepstrgc; /* position of sweep in `strt' */ - //c 白色链表。可回收的对象,会在回收阶段被回收 - //c 所有新建的对象都会暂存在这里,但不会被回收,因为lua有双白色机制 + // 所有新建的对象都会暂存在这里,在GC的清理阶段会增量地遍历整个链表。新建对象会加在最*前面*,见luaC_link() GCObject *rootgc; /* list of all collectable objects */ - //c 保存rootgc中当前回收到的位置,下次从这个位置继续回收 + // 保存rootgc中当前回收到的位置,下次从这个位置继续回收 GCObject **sweepgc; /* position of sweep in `rootgc' */ - //c 灰色链表 + // 灰色链表 GCObject *gray; /* list of gray objects */ - //c 不可被打断的对象的灰色链表,比如LUA_THREAD + // 需要一次性扫描处理的,不可被打断的对象的灰色链表,比如LUA_THREAD GCObject *grayagain; /* list of objects to be traversed atomically */ - //c 弱表 + // 弱表 GCObject *weak; /* list of weak tables (to be cleared) */ - //c 有__gc方法的userdata,会在GC阶段调用__gc释放native侧的引用 - GCObject *tmudata; /* last element of list of userdata to be GC */ + // 有__gc方法的userdata,会在GC阶段调用__gc释放native侧的引用。指向链表最后一个 + GCObject *tmudata; /* last element of list of userdata to be GC */ // taggedmethodudata带__gc的udata + +//endregion 与GC有关 + +//region 与内存管理有关的 + Mbuffer buff; /* temporary buffer for string concatentation */ + // GC开始的阈值, lu_mem GCthreshold; - //c 开始进行GC的阈值,当超过这个值时开始GC + // 开始进行GC的阈值,当超过这个值时开始GC lu_mem totalbytes; /* number of bytes currently allocated */ - //c 当前使用的内存大小的估计值 + // 当前使用的内存大小的估计值 lu_mem estimate; /* an estimate of number of bytes actually in use */ + // 待回收的内存大小 lu_mem gcdept; /* how much GC is `behind schedule' */ - //c 一个百分数,控制下一轮GC开始时机,越大,下次gc开始的时间越长 + // 一个百分数,控制下一轮GC开始时机,越大,离下次gc开始的时间越长 int gcpause; /* size of pause between successive GCs */ - //c 控制GC回收速度 + // 控制GC回收速度\gc的粒度 int gcstepmul; /* GC `granularity' */ + +//endregion 与内存管理有关的 + lua_CFunction panic; /* to be called in unprotected errors */ - //c 注册表 - TValue l_registry; - struct lua_State *mainthread; + + TValue l_registry; //全局唯一的注册表,所有lua_State共享一个 + struct lua_State *mainthread; // 主线程对象,不会被回收 UpVal uvhead; /* head of double-linked list of all open upvalues */ + + // 基本类型的元方法 struct Table *mt[NUM_TAGS]; /* metatables for basic types */ TString *tmname[TM_N]; /* array with tag-method names */ + } global_State; -//c StkId引用的永远是lua_State栈上的内容 +//c StkId引用的永远是lua_State栈上的内容,准确来说是base+bias /* ** `per thread' state */ @@ -157,8 +171,7 @@ struct lua_State { int basehookcount; int hookcount; lua_Hook hook; - //c _G global table - TValue l_gt; /* table of globals */ + TValue l_gt; /* table of globals */ //全局表 _G global table TValue env; /* temporary place for environments */ GCObject *openupval; /* list of open upvalues in this stack */ GCObject *gclist; diff --git a/src/lua51/lstring.c b/src/lua51/lstring.c index cadbb4b..acfd02f 100644 --- a/src/lua51/lstring.c +++ b/src/lua51/lstring.c @@ -63,7 +63,7 @@ static TString *newlstr (lua_State *L, const char *str, size_t l, ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); ts->tsv.len = l; ts->tsv.hash = h; - ts->tsv.marked = luaC_white(G(L)); + ts->tsv.marked = luaC_white(G(L)); // 标记为当前白 ts->tsv.tt = LUA_TSTRING; ts->tsv.reserved = 0; @@ -119,8 +119,8 @@ Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { if (s > MAX_SIZET - sizeof(Udata)) luaM_toobig(L); - // 创建并赋值 u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); + //c udata和普通对象在GC上的区别在于不调用luaC_link,因为不会加在 //c G(L)->rootgc链上,而是加在G(L)->mainthread后面 //c udata标记为 white diff --git a/src/lua51/ltable.c b/src/lua51/ltable.c index 38f4218..20fde8e 100644 --- a/src/lua51/ltable.c +++ b/src/lua51/ltable.c @@ -373,7 +373,7 @@ static void rehash (lua_State *L, Table *t, const TValue *ek) { //c 新建table Table *luaH_new (lua_State *L, int narray, int nhash) { Table *t = luaM_new(L, Table); - luaC_link(L, obj2gco(t), LUA_TTABLE); + luaC_link(L, obj2gco(t), LUA_TTABLE); //标记为白色,并加入rootgc t->metatable = NULL; t->flags = cast_byte(~0); /* temporary values (kept only if some malloc fails) */ @@ -455,7 +455,7 @@ static TValue *newkey (lua_State *L, Table *t, const TValue *key) { //c 赋值 gkey(mp)->value = key->value; gkey(mp)->tt = key->tt; // #define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) luaC_barrierback(L,t); } - // 如果当前table是黑色,新建key时需要进行向后避障,将table记为灰色,加入grayagin链表 + // 如果当前table是黑色,新建key时需要进行向后屏障,将table记为灰色,加入grayagin链表 luaC_barriert(L, t, key); lua_assert(ttisnil(gval(mp))); return gval(mp); diff --git a/src/lua51/lua.c b/src/lua51/lua.c index eaa70a6..9861e60 100644 --- a/src/lua51/lua.c +++ b/src/lua51/lua.c @@ -1,3 +1,5 @@ +#if 0 + #define COMPILE_LUA_C #ifdef COMPILE_LUA_C @@ -395,4 +397,6 @@ int main (int argc, char **argv) { } -#endif
\ No newline at end of file +#endif + +#endif // #if 0
\ No newline at end of file diff --git a/src/lua51/lundump.c b/src/lua51/lundump.c index 564f218..5299f55 100644 --- a/src/lua51/lundump.c +++ b/src/lua51/lundump.c @@ -108,7 +108,7 @@ static void LoadConstants(LoadState* S, Proto* f) for (i=0; i<n; i++) { TValue* o=&f->k[i]; - int t=LoadChar(S); + int t=LoadChar(S); //c 用一个字节标识类型 switch (t) { case LUA_TNIL: diff --git a/src/lua51/lvm.c b/src/lua51/lvm.c index ea1d019..8733b07 100644 --- a/src/lua51/lvm.c +++ b/src/lua51/lvm.c @@ -143,7 +143,7 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ setobj2t(L, oldval, val); h->flags = 0; - luaC_barriert(L, h, val); + luaC_barriert(L, h, val); // 向后屏障 return; } /* else will try the tag method */ @@ -344,6 +344,7 @@ static void Arith (lua_State *L, StkId ra, const TValue *rb, #define runtime_check(L, c) { if (!(c)) break; } //c 获得指令中的某个部分的值,并根据opmode进行校验 +//c 这里的base就是luaV_execute中的base #define RA(i) (base+GETARG_A(i)) /* to be used after possible stack reallocation */ #define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) @@ -373,13 +374,14 @@ static void Arith (lua_State *L, StkId ra, const TValue *rb, } -//c 虚拟机主入口 +//c!! 虚拟机主入口 +//c 在执行前,需要调用luaD_precall()设置虚拟机的指针、栈等,指向这个函数 //c 执行lua函数(不含C函数,C函数在luaD_precall执行) //c 读取字节码并运行,沟通前端和后端的桥梁 //c 在ldo.c->luaD_call函数中执行,前一步是luaD_precall() void luaV_execute (lua_State *L, int nexeccalls) { - //c 管理指令和数据栈的4个变量,会随着函数调用而改变 + //c 管理指令和数据栈的4个变量,会随着函数调用而改变,这样就实现的调用栈 LClosure *cl;// lua closure,包含函数原型、upvalue、环境env StkId base;// 当前调用的base地址 TValue *k;// 当前函数原型里面的常数表 |
