在下面的示例中,(3)的prev成员指向(2),(2)的prev成员指向(1),(1)的prev为NULL。
g = 10 -- (1) 全局环境的 FuncState: 其 prev 为 NULL function fun () -- (2) fun 的 FuncState: 其 prev 指向(1) local a = 2 function test() -- (3) test 的 FuncState: 其 prev 指向(2) local b = 1 -- g -> test -> fun -> 全局环境 -> VGLOBAL -- b -> test ->VLOCAL -- a -> test -> fun -> VUPVAL print(a,b,g) end test() end fun()
Lua解释器在解析完某个环境(比如全局环境)中定义的函数之后,生成的 Proto 结构体数据必然也会存放在该环境的 Proto结构体数组中,最后一并返回。测试代码:
function test() end
ChunkSpy编译出来的指令如下。
; function [0] definition (level 1) ; 0 upvalues, 0 params, 2 stacks .function 0 0 2 2 .const "test" ; 0 ; function [0] definition (level 2) ; 0 upvalues, 0 params, 2 stacks .function 0 0 0 2 [1] return 0 1 ; end of function [1] closure 0 0 ; 0 upvalues #第一层函数即全局环境,用于定义函数; [2] setglobal 0 0 ; test #用于将函数体的信息赋值给第二层的函数test。 [3] return 0 1 ; end of function
函数定义相关指令 OP_CLOSURE 格式如下:
typedef enum { /*A: 存放函数的寄存器 B: Proto数组的索引 C: 无 */ // ...... OP_CLOSURE,/*A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n))*/ // ...... } OpCode;
解释器会依次走过以下函数(箭头左方),右边是其对应的EBNF表示
chunk -> {stat [';']} statement-> body body -> funcstat funcstat -> FUNCTION funcname body
函数调用堆栈,这里的重点是 body 和 funcstat两个函数。
int dofile(const char *filename, const char *cmd); LUALIB_API int luaL_loadfile (lua_State *L, const char *filename); LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname); int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef); int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); static void f_parser (lua_State *L, void *ud); Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name); // EBNF 对应函数 static void chunk (LexState *ls); static int statement (LexState *ls); static void body (LexState *ls, expdesc *e, int needself, int line); static void funcstat (LexState *ls, int line);
函数body处理函数体的解析
static void body (LexState *ls, expdesc *e, int needself, int line) { /* body -> `(' parlist `)' chunk END */ FuncState new_fs; open_func(ls, &new_fs); //主要用于初始化 new_fs //完成函数体的解析,解析结果保存在局部变量 new_fs 的 Proto *f 中 。 new_fs.f->linedefined = line; checknext(ls, '('); if (needself) { new_localvarliteral(ls, "self", 0); adjustlocalvars(ls, 1); } parlist(ls); checknext(ls, ')'); chunk(ls); new_fs.f->lastlinedefined = ls->linenumber; check_match(ls, TK_END, TK_FUNCTION, line); //将最后分析的结果保存到 new_fs 的 Proto 中 close_func(ls); // 主要功能是把 new_fs 的 Proto 指针保存到父函数 FuncState 的 Proto指针的 p 数组中 pushclosure(ls, &new_fs, e); }
pushclosure
static void pushclosure (LexState *ls, FuncState *func, expdesc *v) { FuncState *fs = ls->fs; Proto *f = fs->f; int oldsize = f->sizep; int i; luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, MAXARG_Bx, "constant table overflow"); while (oldsize < f->sizep) f->p[oldsize++] = NULL; f->p[fs->np++] = func->f; //将解析函数体生成的 Proto 指针赋值到父函数 FuncState 的 Proto数组中 luaC_objbarrier(ls->L, f, func->f); // 生成一个OP CLOSURE 指令。这里的类型是需要重定向的,因为后面需要将这个函数体与具体的变量进行绑定 init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); for (i=0; i<func->f->nups; i++) {//将外部引用的对象赋值到当前函数棋中 OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0); } }
用于存放函数信息的结构体是Closure
#define ClosureHeader CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; struct Table *env typedef struct CClosure { ClosureHeader; lua_CFunction f; TValue upvalue[1]; } CClosure; typedef struct LClosure { ClosureHeader; struct Proto *p; //用于存放解析函数体代码之后的指令 UpVal *upvals[1]; //用于保存外部引用的局部变量 } LClosure; typedef union Closure { CClosure c; LClosure l; } Closure;
funcstat 处理函数的定义,即如何把函数体信息和变量结合在一起。
static void funcstat (LexState *ls, int line) { /* funcstat -> FUNCTION funcname body */ int needself; expdesc v, b; // v用来保存函数名信息, b用来保存函数体信息 luaX_next(ls); /* skip FUNCTION */ needself = funcname(ls, &v); // 解析函数名 body(ls, &b, needself, line); // 解析函数体 luaK_storevar(ls->fs, &v, &b); // 前面解析出来的 b 与 v对应上 luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ }
根据变量不同的作用域来生成保存变量的语句
void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { switch (var->k) { case VLOCAL: { freeexp(fs, ex); exp2reg(fs, ex, var->u.s.info); return; } case VUPVAL: { int e = luaK_exp2anyreg(fs, ex); luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0); break; } case VGLOBAL: { int e = luaK_exp2anyreg(fs, ex); // 此例的 test 是全局的,所以会生成一个 OP_SETGLOBAL 语句 luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info); break; } case VINDEXED: { int e = luaK_exp2RK(fs, ex); luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e); break; } default: { lua_assert(0); /* invalid var kind to store */ break; } } freeexp(fs, ex); }
luaK_fixline