閉包(closure):是由一個函數和該函數會訪問到的非局部變量(或者是upvalue)組成的,函數是一個麼有upvalue的閉包
非局部變量:不在自己定義的域中的局部變量
函數與所有其他的值是一樣都是匿名的,即他們沒有名稱。當討論一個函數時(例如print),實質上在討論一個持有某個函數的變量,一個函數定義實質就是一條賦值語句,這條語句創建了一種類型爲“函數”的值,並賦值給一個變量
function
foo(x) print(x) end |
實質是等價於
1
|
foo
= function (x) print(x) end |
執行函數:每當Lua執行一個形如function...end
這樣的表達式時,他就會創建一個新的數據對象,其中包含了相應函數原型的引用及一個由所有upvalue引用組成的數組,而這個數據對象就稱爲閉包。
共享。爲了保證這種唯一性,Lua 爲整個運行棧保存了一個鏈接着所有正打開着
的upvalue(那些當前正指向棧內局部變量的upvalue)的鏈表(圖4 中未決狀態
的局部變量的鏈表)。當Lua 創建一個新的閉包時,它開始遍歷所有的外層局部
變量,對於其中的每一個,若在上述upvalue 鏈表中找到它,就重用此upvalue,
否則,Lua 將創建一個新的upvalue 並加入鏈表中。注意,一般情況下這種遍歷
過程在探查了少數幾個節點後就結束了,因爲對於每個被內層函數用到的外層局
部變量來說,該鏈表至少包含一個與其對應的入口(upvalue)。一旦某個關閉的
upvalue 不再被任何閉包所引用,那麼它的存儲空間就立刻被回收。
- typedef union Closure {
- CClosure c;
- LClosure l;
- } Closure;
LClosure表示lua的函數,這些函數是由lua虛擬機進行管理的..
- #define ClosureHeader \
- CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \
- struct Table *env
nupvalues:表示upvalue或者upvals的大小(閉包和函數裏面的)。
gclist:鏈接到全局的gc鏈表。
env:環境,可以看到它是一個table類型的,他裏面保存了一些全局變量等。
- typedef struct CClosure {
- ClosureHeader;
- lua_CFunction f;
- TValue upvalue[1];
- } CClosure;
lua_CFunction
f: 這個表示所要執行的c函數的原型.
TValue upvalue[1]:這個表示函數運行所需要的一些參數(比如string 的match函數,它所需要的幾個參數都會保存在upvalue裏面
- LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
- Closure *cl;
- lua_lock(L);
- luaC_checkGC(L);
- api_checknelems(L, n);
- ///new一個cclosure
- cl = luaF_newCclosure(L, n, getcurrenv(L));
- cl->c.f = fn;
- L->top -= n;
- ///開始將參數值放到upvalue中.
- while (n--)
- setobj2n(L, &cl->c.upvalue[n], L->top+n);
- setclvalue(L, L->top, cl);
- lua_assert(iswhite(obj2gco(cl)));
- api_incr_top(L);
- lua_unlock(L);
- }
new 閉包結構 賦值c傳遞過來的指針 取出壓入棧中的參數
- typedef struct LClosure {
- ClosureHeader;
- struct Proto *p;
- UpVal *upvals[1];
- } LClosure;
struct
Proto *p:這個指針包含了很多的屬性,比如變量,比如嵌套函數等等。
UpVal *upvals[1]:這個數組保存了指針 指針指向UpVal 。
當前的堆棧lua_State中它裏面包含有GCObject 類型的域叫openupval這個域也就是當前的棧上的所有open的uvalue lua_State 有global_State域
全局堆棧global_State中的UpVal
uvhead則是整個lua虛擬機裏面所有棧的upvalue鏈表的頭
- UpVal *luaF_findupval (lua_State *L, StkId level) {
- global_State *g = G(L);
- ///得到openupval鏈表
- GCObject **pp = &L->openupval;
- UpVal *p;
- UpVal *uv;
- ///開始遍歷open upvalue。
- while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {
- lua_assert(p->v != &p->u.value);
- ///發現已存在。
- if (p->v == level) {
- if (isdead(g, obj2gco(p))) /* is it dead? */
- changewhite(obj2gco(p)); /* ressurect it */
- ///直接返回
- return p;
- }
- pp = &p->next;
- }
- ///否則new一個新的upvalue
- uv = luaM_new(L, UpVal); /* not found: create a new one */
- uv->tt = LUA_TUPVAL;
- uv->marked = luaC_white(g);
- ///設置值
- uv->v = level; /* current value lives in the stack */
- ///首先插入到lua_state的openupval域
- uv->next = *pp; /* chain it in the proper position */
- *pp = obj2gco(uv);
- ///然後插入到global_State的uvhead(這個也就是雙向鏈表的頭)
- uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */
- uv->u.l.next = g->uvhead.u.l.next;
- uv->u.l.next->u.l.prev = uv;
- g->uvhead.u.l.next = uv;
- lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);
- return uv;
- }
查找openvalue 找到則返回 找不到新建一個upvalue 添加到openupval鏈表中和 global_State的uvhead(這個也就是雙向鏈表的頭)
- void luaF_close (lua_State *L, StkId level) {
- UpVal *uv;
- global_State *g = G(L);
- ///開始遍歷open upvalue
- while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {
- GCObject *o = obj2gco(uv);
- lua_assert(!isblack(o) && uv->v != &uv->u.value);
- L->openupval = uv->next; /* remove from `open' list */
- if (isdead(g, o))
- luaF_freeupval(L, uv); /* free upvalue */
- else {
- ///unlink掉當前的uv.
- unlinkupval(uv);
- setobj(L, &uv->u.value, uv->v);
- uv->v = &uv->u.value; /* now current value lives here */
- luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */
- }
- }
- }
- 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 */
- uv->u.l.prev->u.l.next = uv->u.l.next;
- }
從兩個列表中移除upvalue
原文點擊打開鏈接