Lua5.3 VM 分析(七)生成闭包
来源:互联网 发布:已备案域名已删除 编辑:程序博客网 时间:2024/05/29 08:42
Lua5.3 VM 分析(七)生成闭包
在Lua 中,函数是一等公民。一切代码都是函数,准确的说是闭包。当我们执行一段程序时,其实就是调用一个函数。加载一个库,也是调用一个函数。加载一个Lua 源文件,里面即使定义了很多 Lua 函数,但是 它整体依旧是单个函数。
所以,每段完整的字节码都是一个Lua 函数。而每个函数里可以附有很多个函数原型 Proto。函数原型 Proto 没有放在常量表中,而是单独成表。这是因为,它们不可以被Lua 代码直接使用。只有 Proto 和 upvalue 绑定在一起时,形成闭包,才是 Lua VM 可以处理的 执行对象。
函数原型在生成包含它们的函数的代码被加载时,在内存中生成 Proto 对象。单个函数原型可以被多次绑定,生成多个闭包对象。这个过程由 CLOSURE 操作完成。
vmcase(OP_CLOSURE) { printf("OP_CLOSURE \n"); Proto *p = cl->p->p[GETARG_Bx(i)]; LClosure *ncl = getcached(p, cl->upvals, base); /* cached closure */ if (ncl == NULL) /* no match? */ pushclosure(L, p, cl->upvals, base, ra); /* create a new one */ else setclLvalue(L, ra, ncl); /* push cashed closure */ checkGC(L, ra + 1); vmbreak; }
在生成闭包的过程中,首先调用 getcached 函数,从缓存中取上次生成的闭包,如果可能,就重复利用。这对函数式编程特别有效,因为当你返回一个没有任何 upvalue 的纯函数,或是只绑定有全局变量的函数时,不会生成新的闭包 实例。
getcached 函数 用来检查是否有缓存过的闭包,以及这次需要绑定的 upvalue 是否和缓存中的 proto 一致。
static LClosure *getcached (Proto *p, UpVal **encup, StkId base) { LClosure *c = p->cache; if (c != NULL) { /* is there a cached closure? */ int nup = p->sizeupvalues; Upvaldesc *uv = p->upvalues; int i; for (i = 0; i < nup; i++) { /* check whether it has right upvalues */ TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v; if (c->upvals[i]->v != v) return NULL; /* wrong upvalue; cannot reuse closure */ } } return c; /* return cached closure (or NULL if no cached closure) */}
函数原型 Proto 中记录了 upvalue的描述信息 Upvaldesc 结构 用来让 upvalue 可以比较。
typedef struct Upvaldesc { TString *name; /* upvalue name (for debug information) */ lu_byte instack; /* whether it is in stack */ lu_byte idx; /* index of upvalue (in stack or in outer function's list) */} Upvaldesc;
比较引用的 upvalue是否相同,按instack 标记分开处理。
相同的 upvalue 地址也一定相同。全部 upvalue 都一致的情况下,说明缓存的闭包就可以复用。此时,只需要调用 setclLvalue 把他赋给 ra 即可。
反之,缓存闭包不可复用,需要调用 pushclosure 函数生成一个新的闭包,并更新缓存。
static void pushclosure (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 */}
绑定 upvalue 生成闭包过程 之后,在函数最后,在更新 cache 指针之前,需要调用判断 Proto 是否需要 GC。
- Lua5.3 VM 分析(七)生成闭包
- Lua5.3 VM 分析(一)字节码运行
- Lua5.3 VM 分析(二)表处理
- Lua5.3 VM 分析(三)表达式运算
- Lua5.3 VM 分析(四)分支和跳转
- Lua5.3 VM 分析(八)For 循环
- Lua5.3 虚拟机指令分析(一)概述
- Lua5.3 虚拟机指令分析(二)赋值指令
- Lua5.3 虚拟机指令分析(三)表达式运算
- Lua5.3 虚拟机指令分析(四)分支与跳转
- Lua5.3 虚拟机指令分析(五)函数调用
- Lua5.3 虚拟机指令分析(六)不定参数
- Lua5.3 虚拟机指令分析(八)循环
- Lua5.3 虚拟机指令分析(十)表相关指令
- Lua5.1代码阅读(七):lvm.h/lvm.c
- 漫步凸分析七——凸函数闭包
- Lua 5.3 源码分析 (七) 闭包 Closure
- Swift中文教程(七) 闭包
- 1037. 在霍格沃茨找零钱(20)
- 如何更好的做计划-SMART原则
- Lua5.3 VM 分析(三)表达式运算
- 【数学基础】【欧拉函数解析模板】【欧拉筛法实现求1~n】【求单个n】
- Lua5.3 VM 分析(四)分支和跳转
- Lua5.3 VM 分析(七)生成闭包
- Lua5.3 VM 分析(八)For 循环
- linearlayout之margin和peddling
- talk is expensive , Don't show me your code at that time
- Linux 下如何查看cpu信息
- swift3自定义UIBotton(带动画效果)
- Activity 小结
- var_dump和var_export
- 第三篇 中国近现代史纲要