Lua upvalue 函数 闭包

闭包(closure):是由一个函数和该函数会访问到的非局部变量(或者是upvalue)组成的,函数是一个么有upvalue的闭包

非局部变量:不在自己定义的域中的局部变量


函数与所有其他的值是一样都是匿名的,即他们没有名称。当讨论一个函数时(例如print),实质上在讨论一个持有某个函数的变量,一个函数定义实质就是一条赋值语句,这条语句创建了一种类型为“函数”的值,并赋值给一个变量

function foo(x) print(x) end

实质是等价于

?
1
foo = function (x) print(x) end
编译函数:会为他生成一个原型(prototype),其中包含了函数体对应的 虚拟机 指令、函数用到的常量值(数,文本字符串等等)和一些调试信息。在运行时,

执行函数:每当Lua执行一个形如function...end 这样的表达式时,他就会创建一个新的数据对象,其中包含了相应函数原型的引用及一个由所有upvalue引用组成的数组,而这个数据对象就称为闭包。



扫描二维码关注公众号,回复: 1567817 查看本文章
引用
通过为每个变量至少创建一个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



原文点击打开链接

猜你喜欢

转载自blog.csdn.net/dingqinghui/article/details/78027473