函数是所有编程语言的执行单元或者说是行为,函数编译之后由一系列数据组成,比如指令集数组、常量数组、调试信息、本地变量、起始行信息等。lua也一样,函数闭包也是lua中执行单元,lua字节码执行过程中产生的数据结构。通过结构体定义可以看出由函数原型和upvalue数组组成,lua闭包=函数原型+upvalue。而函数原型为编译阶段生成的。分为CClosure和LClosure。
1).具体结构如下:
#define ClosureHeader \
CommonHeader; lu_byte nupvalues; GCObject *gclist
typedef struct CClosure {
ClosureHeader;
lua_CFunction f;
TValue upvalue[1]; /* list of upvalues */
} CClosure;
typedef struct LClosure {
ClosureHeader;
struct Proto *p;
UpVal *upvals[1]; /* list of upvalues */
} LClosure;
typedef union Closure {
CClosure c;
LClosure l;
} Closure;
2).闭包的生成过程:
*lauxlib.h/lauxlib.c:luaL_dofile-->luaL_loadfile-->luaL_loadfilex-->lua_load(lapi.c)
*lapi.c:luaD_protectedparser
*ldo.c:luaD_protectedparser-->f_parser-->luaY_parser(文本)/luaU_undump(二进制)
lundump.c:
1.luaU_undump-->luaF_newLclosure:创建一个 closure, 压入栈顶
LClosure *luaF_newLclosure (lua_State *L, int nupvals) {
GCObject *o = luaC_newobj(L, LUA_VLCL, sizeLclosure(nupvals));
LClosure *c = gco2lcl(o);
c->p = NULL;
c->nupvalues = cast_byte(nupvals);
while (nupvals--) c->upvals[nupvals] = NULL;
return c;
}
2.loadFunction:加载Proto(函数原型)
static void loadFunction (LoadState *S, Proto *f, TString *psource) {
f->source = loadStringN(S, f);
if (f->source == NULL) /* no source in dump? */
f->source = psource; /* reuse parent's source */
f->linedefined = loadInt(S);
f->lastlinedefined = loadInt(S);
f->numparams = loadByte(S);
f->is_vararg = loadByte(S);
f->maxstacksize = loadByte(S);
loadCode(S, f);
loadConstants(S, f);
loadUpvalues(S, f);
loadProtos(S, f);
loadDebug(S, f);
}
Proto函数原型,编译生成产物:
typedef struct Proto {
CommonHeader;
lu_byte numparams; /* number of fixed (named) parameters */
lu_byte is_vararg;
lu_byte maxstacksize; /* number of registers needed by this function */
int sizeupvalues; /* size of 'upvalues' */
int sizek; /* size of 'k' */
int sizecode;
int sizelineinfo;
int sizep; /* size of 'p' */
int sizelocvars;
int sizeabslineinfo; /* size of 'abslineinfo' */
int linedefined; /* debug information */
int lastlinedefined; /* debug information */
TValue *k; /* constants used by the function */
Instruction *code; /* opcodes */
struct Proto **p; /* functions defined inside the function */
Upvaldesc *upvalues; /* upvalue information */
ls_byte *lineinfo; /* information about source lines (debug information) */
AbsLineInfo *abslineinfo; /* idem */
LocVar *locvars; /* information about local variables (debug information) */
TString *source; /* used for debug information */
GCObject *gclist;
} Proto;
小结:以上为函数闭包的几个核心数据部分。函数原型是编译生成的产物而函数闭包是脚本原型是构建出来的,由数据原型和upvalue组成,内部核心结构:Proto, CallInfo。