Lua upvalue 函數 閉包

閉包(closure):是由一個函數和該函數會訪問到的非局部變量(或者是upvalue)組成的,函數是一個麼有upvalue的閉包

非局部變量:不在自己定義的域中的局部變量


函數與所有其他的值是一樣都是匿名的,即他們沒有名稱。當討論一個函數時(例如print),實質上在討論一個持有某個函數的變量,一個函數定義實質就是一條賦值語句,這條語句創建了一種類型爲“函數”的值,並賦值給一個變量

function foo(x) print(x) end

實質是等價於

?
1
foo = function (x) print(x) end
編譯函數:會爲他生成一個原型(prototype),其中包含了函數體對應的虛擬機指令、函數用到的常量值(數,文本字符串等等)和一些調試信息。在運行時,

執行函數:每當Lua執行一個形如function...end 這樣的表達式時,他就會創建一個新的數據對象,其中包含了相應函數原型的引用及一個由所有upvalue引用組成的數組,而這個數據對象就稱爲閉包。



引用
通過爲每個變量至少創建一個upvalue 並按所需情況進行重複利用,保證了未決狀態(是否超過生存期)的局部變量(pending vars)能夠在閉包間正確地 
共享
。爲了保證這種唯一性,Lua 爲整個運行棧保存了一個鏈接着所有正打開着 
的upvalue(那些當前正指向棧內局部變量的upvalue)的鏈表(圖4 中未決狀態 
的局部變量的鏈表)。當Lua 創建一個新的閉包時,它開始遍歷所有的外層局部 
變量,對於其中的每一個,若在上述upvalue 鏈表中找到它,就重用此upvalue, 
否則,Lua 將創建一個新的upvalue 並加入鏈表中。注意,一般情況下這種遍歷 
過程在探查了少數幾個節點後就結束了,因爲對於每個被內層函數用到的外層局 
部變量來說,該鏈表至少包含一個與其對應的入口(upvalue)。一旦某個關閉的 
upvalue 不再被任何閉包所引用,那麼它的存儲空間就立刻被回收。

源碼
  1. typedef union Closure {  
  2.   CClosure c;  
  3.   LClosure l;  
  4. } Closure;  
CClosure表示是c函數,也就是和lua外部交互傳遞進來的c函數以及內部所使用的c函數. 
LClosure表示lua的函數,這些函數是由lua虛擬機進行管理的..


  1. #define ClosureHeader \  
  2.     CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \  
  3.     struct Table *env  


isC:如果是c函數這個值爲1,爲lua的函數則爲0. 
nupvalues:表示upvalue或者upvals的大小(閉包和函數裏面的)。 
gclist:鏈接到全局的gc鏈表。 
env:環境,可以看到它是一個table類型的,他裏面保存了一些全局變量等。 



  1. typedef struct CClosure {  
  2.   ClosureHeader;  
  3.   lua_CFunction f;  
  4.   TValue upvalue[1];  
  5. } CClosure;  

lua_CFunction f: 這個表示所要執行的c函數的原型. 
TValue upvalue[1]:這個表示函數運行所需要的一些參數(比如string 的match函數,它所需要的幾個參數都會保存在upvalue裏面
 


  1. LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {  
  2.   Closure *cl;  
  3.   lua_lock(L);  
  4.   luaC_checkGC(L);  
  5.   api_checknelems(L, n);  
  6. ///new一個cclosure  
  7.   cl = luaF_newCclosure(L, n, getcurrenv(L));  
  8.   cl->c.f = fn;  
  9.   L->top -= n;  
  10. ///開始將參數值放到upvalue中.  
  11.   while (n--)  
  12.     setobj2n(L, &cl->c.upvalue[n], L->top+n);  
  13.   setclvalue(L, L->top, cl);  
  14.   lua_assert(iswhite(obj2gco(cl)));  
  15.   api_incr_top(L);  
  16.   lua_unlock(L);  
  17. }

new 閉包結構  賦值c傳遞過來的指針 取出壓入棧中的參數 


  1. typedef struct LClosure {  
  2.   ClosureHeader;  
  3.   struct Proto *p;  
  4.   UpVal *upvals[1];  
  5. } LClosure;  

struct Proto *p:這個指針包含了很多的屬性,比如變量,比如嵌套函數等等。 
UpVal *upvals[1]:這個數組保存了指針 指針指向UpVal 。 


當前的堆棧lua_State中它裏面包含有GCObject 類型的域叫openupval這個域也就是當前的棧上的所有open的uvalue   lua_State 有global_State域

全局堆棧global_State中的UpVal uvhead則是整個lua虛擬機裏面所有棧的upvalue鏈表的頭

  1. UpVal *luaF_findupval (lua_State *L, StkId level) {  
  2.   global_State *g = G(L);  
  3. ///得到openupval鏈表  
  4.   GCObject **pp = &L->openupval;  
  5.   UpVal *p;  
  6.   UpVal *uv;  
  7. ///開始遍歷open upvalue。  
  8.   while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) {  
  9.     lua_assert(p->v != &p->u.value);  
  10. ///發現已存在。  
  11.     if (p->v == level) {    
  12.       if (isdead(g, obj2gco(p)))  /* is it dead? */  
  13.         changewhite(obj2gco(p));  /* ressurect it */  
  14. ///直接返回  
  15.       return p;  
  16.     }  
  17.     pp = &p->next;  
  18.   }  
  19. ///否則new一個新的upvalue  
  20.   uv = luaM_new(L, UpVal);  /* not found: create a new one */  
  21.   uv->tt = LUA_TUPVAL;  
  22.   uv->marked = luaC_white(g);  
  23. ///設置值  
  24.   uv->v = level;  /* current value lives in the stack */  
  25. ///首先插入到lua_state的openupval域  
  26.   uv->next = *pp;  /* chain it in the proper position */  
  27.   *pp = obj2gco(uv);  
  28. ///然後插入到global_State的uvhead(這個也就是雙向鏈表的頭)  
  29.   uv->u.l.prev = &g->uvhead;  /* double link it in `uvhead' list */  
  30.   uv->u.l.next = g->uvhead.u.l.next;  
  31.   uv->u.l.next->u.l.prev = uv;  
  32.   g->uvhead.u.l.next = uv;  
  33.   lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);  
  34.   return uv;  
  35. }  

查找openvalue 找到則返回 找不到新建一個upvalue 添加到openupval鏈表中和 global_State的uvhead(這個也就是雙向鏈表的頭)



  1. void luaF_close (lua_State *L, StkId level) {  
  2.   UpVal *uv;  
  3.   global_State *g = G(L);  
  4. ///開始遍歷open upvalue  
  5.   while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) {  
  6.     GCObject *o = obj2gco(uv);  
  7.     lua_assert(!isblack(o) && uv->v != &uv->u.value);  
  8.     L->openupval = uv->next;  /* remove from `open' list */  
  9.     if (isdead(g, o))  
  10.       luaF_freeupval(L, uv);  /* free upvalue */  
  11.     else {  
  12. ///unlink掉當前的uv.  
  13.       unlinkupval(uv);  
  14.       setobj(L, &uv->u.value, uv->v);  
  15.       uv->v = &uv->u.value;  /* now current value lives here */  
  16.       luaC_linkupval(L, uv);  /* link upvalue into `gcroot' list */  
  17.     }  
  18.   }  
  19. }  
  20.   
  21. static void unlinkupval (UpVal *uv) {  
  22.   lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv);  
  23.   uv->u.l.next->u.l.prev = uv->u.l.prev;  /* remove from `uvhead' list */  
  24.   uv->u.l.prev->u.l.next = uv->u.l.next;  
  25. }  

從兩個列表中移除upvalue



原文點擊打開鏈接

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章