structLClosure *cache;/* last-created closure with this prototype */ GCObject *gclist; } Proto;
我们更关注的是 Upvalues。
Upvalues
upvalue 主要由 一个union 和 TValue 构成,在这里要理解一个概念。
upvalue 的 open 状态。
open:当我们说一个 upvalue 是 open 的,指的是这个 upvalue 其原始值还在数据栈上(因此这个对象如果是可回收的,则被扫描标记管理)。
close:如果说一个 upvalue 是 close 的,指的是这个 upvalue 已经不在栈上了,离开了作用域,会被拷贝到 UpVal.u.value 中,不受到垃圾回收的管控,而是被引用计数管理。
1 2 3 4 5 6 7 8 9 10 11 12 13
structUpVal { TValue *v; /* points to stack or to its own value */ lu_mem refcount; /* reference counter */ union { struct {/* (when open) */ UpVal *next; /* linked list */ int touched; /* mark to avoid cycles with dead threads */ } open; TValue value; /* the value (when closed) */ } u; };
#define upisopen(up) ((up)->v != &(up)->u.value)
因此 当 upvalue 为 open 时,v 指向 栈上原始值的地址。反之,则将其值存入到 UpVal 这个结构体自身。
这也就是为什么 下面的代码能够正确执行的原因。
1 2 3 4 5 6 7
functionCounter() local t = 0 returnfunction() t = t + 1 return t end end
return 回去这个 function 因为 t 已经不在栈上了,故将其值存入了这个 UpVal 结构体中,跟随着这个 function 一起。
结构中的 open 这一个结构体,则是当 UpVal 为 open态时,链接上所有的 open UpVal,方便后续的查找,而 touched 是为了防止垃圾回收时 还指向栈上对象的 upvalue 被清理。因为 垃圾回收的 atomic 有个 remarkupval 的函数,在里面进行重新标记 upvalue 。
Closure
无论是 C 函数,还是 Lua 函数,其 UpValues 都与函数本身分离,但又被包裹在一个结构体中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
typedefstructCClosure { ClosureHeader; lua_CFunction f; TValue upvalue[1]; /* list of upvalues */ } CClosure;
typedefstructLClosure { ClosureHeader; structProto *p; UpVal *upvals[1]; /* list of upvalues */ } LClosure;
staticvoidpushclosure(lua_State *L, Proto *p, UpVal **encup, StkId base, StkId ra) { int nup = p->sizeupvalues; Upvaldesc *uv = p->upvalues; int i; LClosure *ncl = luaF_newLclosure(L, nup); ncl->p = p; setclLvalue(L, ra, ncl); /* anchor new closure in stack */ for (i = 0; i < nup; i++) { /* fill in its upvalues */ if (uv[i].instack) /* upvalue refers to local variable? */ ncl->upvals[i] = luaF_findupval(L, base + uv[i].idx); else/* get upvalue from enclosing function */ ncl->upvals[i] = encup[uv[i].idx]; ncl->upvals[i]->refcount++; /* new closure is white, so we do not need a barrier here */ } if (!isblack(p)) /* cache will not break GC invariant? */ p->cache = ncl; /* save it on cache for reuse */ }
UpVal *luaF_findupval(lua_State *L, StkId level) { UpVal **pp = &L->openupval; UpVal *p; UpVal *uv; lua_assert(isintwups(L) || L->openupval == NULL); while (*pp != NULL && (p = *pp)->v >= level) { lua_assert(upisopen(p)); if (p->v == level) /* found a corresponding upvalue? */ return p; /* return it */ pp = &p->u.open.next; } /* not found: create a new upvalue */ uv = luaM_new(L, UpVal); uv->refcount = 0; uv->u.open.next = *pp; /* link it to list of open upvalues */ uv->u.open.touched = 1; *pp = uv; uv->v = level; /* current value lives in the stack */ if (!isintwups(L)) { /* thread not in list of threads with upvalues? */ L->twups = G(L)->twups; /* link it to the list */ G(L)->twups = L; } return uv; }