在singlevaraux函数中会判断变量是local、upvalue还是global的。如果fs为null了则说明变量为全局的,否则进入searchvar在当前的函数局部变量数组中查找,否则根据fs的prev成员取得其父函数的FuncState并传入singlevaraux中递归查找,如果前面的都没满足则变量为upvlaue。此例中进入第21行中,由于fs已经指向了main func因此其prev为null,“foo”判定为global并返回到exprstate函数中。在取得了“foo”的信息后,因为“foo”不是函数调用,因此接着进入assignment函数中
1 primaryexp(ls, &v.v); 2 if (v.v.k == VCALL) /* stat -> func */ 3 SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ 4 else { /* stat -> assignment */ 5 v.prev = NULL; 6 assignment(ls, &v, 1); 7 }
在assignment函数中首先判断下一个token是否为“,",此例中不是则说明是单变量的赋值,接着check下一个token为”=“,成立,接着调用explist1判断等号右边有几个值,此例为1个,然后会判断左边的变量数是否等于右边的值数,不等于则进入adjust_assign函数进行调整,此例是相等的因此依次进入luaK_setoneret和luaK_storevar函数。在luaK_storevar中首先进入int e = luaK_exp2anyreg(fs, ex);函数luaK_exp2anyreg的K代表了此函数是字节码相关的函数,ex为值”bar“,这个函数又调用了discharge2reg,根据ex的类型来生成不同的字节码:
1 static void discharge2reg (FuncState *fs, expdesc *e, int reg) { 2 luaK_dischargevars(fs, e); 3 switch (e->k) { 4 case VNIL: { 5 luaK_nil(fs, reg, 1); 6 break; 7 } 8 case VFALSE: case VTRUE: { 9 luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); 10 break; 11 } 12 case VK: { 13 luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); 14 break; 15 } 16 //... ... 17 }
由于”bar“是常量因此调用luaK_codeABx函数生成loadk字节码。reg为保存载入的常量值的寄存器号,e->u.s.info根据不同类型值代表不同含义,根据注释我们知道此时info为常量数组的下标。
typedef enum { //... ... VK, /* info = index of constant in `k' */ VKNUM, /* nval = numerical value */ VLOCAL, /* info = local register */ VGLOBAL, /* info = index of table; aux = index of global name in `k' */ //... ... } expkind;
生成了loadk后返回到上面的函数中接着进入luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info);其中e为luaK_exp2anyreg的返回值表示常量保存在的寄存器标号,info根据注释当为global类型时表示global table的相应下标,因此luaK_codeABx函数将生成setglobal字节码,将刚刚用loadk将常量加载到寄存器中的值保存到global table相应的位置上。因此foo = "bar"语句就完整的生成了相应的字节码了。
接下来将生成local a,b = "a","b"语句的字节码了。过程大致相同,不同的是a,b是local变量且这个赋值语句是多变量赋值语句,因此前面的函数会用LHS_assign链表将a,b变量连接起来。如图所示:
CentOS 编译安装 Lua LuaSocket