Lua源码解析之三:code
来源:互联网 发布:淘宝卖怎么开通淘金币 编辑:程序博客网 时间:2024/06/01 08:00
1. 前言
何为CODE?在Proto这一结构体中,有一个字段code:
/*** Function Prototypes*/typedef struct Proto { CommonHeader; lu_byte numparams; /* number of fixed parameters */ lu_byte is_vararg; /* 2: declared vararg; 1: uses 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 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 */ int *lineinfo; /* map from opcodes to source lines (debug information) */ LocVar *locvars; /* information about local variables (debug information) */ Upvaldesc *upvalues; /* upvalue information */ struct LClosure *cache; /* last-created closure with this prototype */ TString *source; /* used for debug information */ GCObject *gclist;} Proto;
用于保存函数运行时的指令。Lua在解析函数的过程中,会将一条条语句逐个‘编译’成指令,这些指令就存放在Proto->code这个成员里。除了code成员,还有几个重要的成员变量:
sizecode: 本函数的指令个数
numparams: 定参个数
is_vararg: 是否支持变参:1 表示使用了变参作为最后一个参数
maxstacksize: 本函数最多使用了多少个寄存器
k: 常量表
sizek: 常量表大小
p: 子函数表
sizep: 子函数大小
在解析整个函数结束后,会生成一个常量表,用于保存函数体内的常量字符串和数字,指令放在code里面,函数体内的子函数放在p数组里。
在上一篇中,介绍了函数的解析,包括词法和语法分析(语法分析只是讲了个大概: ) ),本篇将从解析语句开始,洞悉Lua源代码如何一边解析,一边生成code。
2. 解析语句(statement)
2.0 instructor的内部表达
在“Lua源码解析之一”中,有介绍到,lua的指令(instructor),lua指令由一个32位的unsigned int组成,其中:
6位用于表示OPCODE,即操作指令
8位用于表示操作数A,9位用于表示操作数B,9位用于表示操作数C
为了方面进行位操作,lopcodes.h定义了一系列的宏和辅助函数,非常方便使用,假如你想生成一条指令:其命令码为opcode,操作码分别为A,B,C,可以这样写:
luaK_codeABC(fs, opcode, A, B, C);
宏CREATE_ABC(o, a, b, c)可以直接生成这个int32的整形数值。
在lcode.c文件中,大部分函数都对特定的指令进行了编码。
有关这些指令的编码,后面会有详解。
2.1 if statement
先来看看if-elseif-then,这样的语句是如何处理的:
static void ifstat (LexState *ls, int line) { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ FuncState *fs = ls->fs; int escapelist = NO_JUMP; /* exit list for finished parts */ test_then_block(ls, &escapelist); /* IF cond THEN block */ while (ls->t.token == TK_ELSEIF) test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ if (testnext(ls, TK_ELSE)) block(ls); /* 'else' part */ check_match(ls, TK_END, TK_IF, line); luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */}
先处理if-then语句,然后再检测,如果是elseif的化,接着处理elseif-then语句,直到遇到else-end语句。
接着查看test_then_block代码
/* * 解析 if-then 语句 */static void test_then_block (LexState *ls, int *escapelist) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ BlockCnt bl; FuncState *fs = ls->fs; expdesc v; int jf; /* instruction to skip 'then' code (if condition is false) */ luaX_next(ls); /* skip IF or ELSEIF */ expr(ls, &v); /* read condition */ checknext(ls, TK_THEN); if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */ gotostat(ls, v.t); /* handle goto/break */ skipnoopstat(ls); /* skip other no-op statements */ if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ leaveblock(fs); return; /* and that is it */ } else /* must skip over 'then' part if condition is false */ jf = luaK_jump(fs); } else { /* regular case (not goto/break) */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ enterblock(fs, &bl, 0); /* 设定if判定为false时需要执行的JMP指令(所对应的pc)*/ jf = v.f; } statlist(ls); /* 'then' part */ leaveblock(fs); if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ /* 如果有else或者elseif则需要insert JMP指令,该JMP指令用于跳出接下来的语句 */ luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ luaK_patchtohere(fs, jf);}
先跳过token-if,接着读取条件控制表达式expr,将结果保存到v,仔细看看expr:
static void expr (LexState *ls, expdesc *v) { subexpr(ls, v, 0);}
它直接调用subexpr:
/*** subexpr -> (simpleexp | unop subexpr) { binop subexpr }** where 'binop' is any binary operator with a priority higher than 'limit'*/static BinOpr subexpr (LexState *ls, expdesc *v, int limit) { BinOpr op; UnOpr uop; enterlevel(ls); uop = getunopr(ls->t.token); if (uop != OPR_NOUNOPR) { int line = ls->linenumber; luaX_next(ls); subexpr(ls, v, UNARY_PRIORITY); luaK_prefix(ls->fs, uop, v, line); } else simpleexp(ls, v); /* expand while operators have priorities higher than 'limit' */ op = getbinopr(ls->t.token); while (op != OPR_NOBINOPR && priority[op].left > limit) { expdesc v2; BinOpr nextop; int line = ls->linenumber; luaX_next(ls); luaK_infix(ls->fs, op, v); /* read sub-expression with higher priority */ nextop = subexpr(ls, &v2, priority[op].right); luaK_posfix(ls->fs, op, v, &v2, line); op = nextop; } leavelevel(ls); return op; /* return first untreated operator */}
查看函数头里面的推导式:subexpr -> (simpleexp | unop subexpr) { binop subexpr },如果前面读到的是一个一元操作符,先利用unop subexpr进行推导,与之对应的代码是:
uop = getunopr(ls->t.token); if (uop != OPR_NOUNOPR) { int line = ls->linenumber; luaX_next(ls); subexpr(ls, v, UNARY_PRIORITY); luaK_prefix(ls->fs, uop, v, line); } else simpleexp(ls, v);
获取到unop之后接着递归调用subexpr,无论如何,递归最终还是得调用simpleexp来终止:
static void simpleexp (LexState *ls, expdesc *v) { /* simpleexp -> FLT | INT | STRING | NIL | TRUE | FALSE | ... | constructor | FUNCTION body | suffixedexp */ switch (ls->t.token) { case TK_FLT: { init_exp(v, VKFLT, 0); v->u.nval = ls->t.seminfo.r; break; } case TK_INT: { init_exp(v, VKINT, 0); v->u.ival = ls->t.seminfo.i; break; } case TK_STRING: { codestring(ls, v, ls->t.seminfo.ts); break; } case TK_NIL: { init_exp(v, VNIL, 0); break; } case TK_TRUE: { init_exp(v, VTRUE, 0); break; } case TK_FALSE: { init_exp(v, VFALSE, 0); break; } case TK_DOTS: { /* vararg */ FuncState *fs = ls->fs; check_condition(ls, fs->f->is_vararg, "cannot use '...' outside a vararg function"); fs->f->is_vararg = 1; /* function actually uses vararg */ init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); break; } case '{': { /* constructor */ constructor(ls, v); return; } case TK_FUNCTION: { luaX_next(ls); body(ls, v, 0, ls->linenumber); return; } default: { suffixedexp(ls, v); return; } } luaX_next(ls);}
simpleexpr中对常量的处理都很简单,直接赋值到v即可。
需注意的是,如果token=TK_STRING,则需要调用luaK_stringK将此string添加到常量表中,并返回其下标。
如果是TK_DOT,表示读到的token为”…”,设定fs->f->is_vararg =1,并设定v的类型为VVARARG。
如果是’{‘,则说明该表达式是一个table的constructor。
2.1.1 table constructor
本小节单独分析constructor是如何解析和编码的:
static void constructor (LexState *ls, expdesc *t) { /* constructor -> '{' [ field { sep field } [sep] ] '}' sep -> ',' | ';' */ FuncState *fs = ls->fs; int line = ls->linenumber; int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); struct ConsControl cc; cc.na = cc.nh = cc.tostore = 0; cc.t = t; init_exp(t, VRELOCABLE, pc); init_exp(&cc.v, VVOID, 0); /* no value (yet) */ luaK_exp2nextreg(ls->fs, t); /* fix it at stack top */ checknext(ls, '{'); do { lua_assert(cc.v.k == VVOID || cc.tostore > 0); if (ls->t.token == '}') break; closelistfield(fs, &cc); field(ls, &cc); } while (testnext(ls, ',') || testnext(ls, ';')); check_match(ls, '}', '{', line); lastlistfield(fs, &cc); SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */}
它先调用luaK_codeABC生成一条OP_NEWTABLE指令,接着初始化table的数组大小和hash表大小为0,调用init_exp设定t的类型为VRELOCABLE,value=pc,顾名思义,VRELOCABLE,表示该表达式的存放在哪,暂时还没确定下来,需要“重定向”(RELOCABLE)。
为了给t安排一个“好住处”,遇到luaK_exp2nextreg:
void luaK_exp2nextreg (FuncState *fs, expdesc *e) { luaK_dischargevars(fs, e); freeexp(fs, e); luaK_reserveregs(fs, 1); exp2reg(fs, e, fs->freereg - 1);}
freeexp释放e所占用的寄存器,luaK_reserveregs只是简单的将freereg++,重点看下exp2reg函数:
/* * */static void exp2reg (FuncState *fs, expdesc *e, int reg) { discharge2reg(fs, e, reg); if (e->k == VJMP) luaK_concat(fs, &e->t, e->u.info); /* put this jump in 't' list */ if (hasjumps(e)) { int final; /* position after whole expression */ int p_f = NO_JUMP; /* position of an eventual LOAD false */ int p_t = NO_JUMP; /* position of an eventual LOAD true */ if (need_value(fs, e->t) || need_value(fs, e->f)) { int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); p_f = code_label(fs, reg, 0, 1); p_t = code_label(fs, reg, 1, 0); luaK_patchtohere(fs, fj); } final = luaK_getlabel(fs); patchlistaux(fs, e->f, final, reg, p_f); patchlistaux(fs, e->t, final, reg, p_t); } e->f = e->t = NO_JUMP; e->u.info = reg; e->k = VNONRELOC;}/* * 将表达式e'安置'到寄存器中 */static void discharge2reg (FuncState *fs, expdesc *e, int reg) { luaK_dischargevars(fs, e); switch (e->k) { case VNIL: { luaK_nil(fs, reg, 1); break; } case VFALSE: case VTRUE: { luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); break; } case VK: { luaK_codek(fs, reg, e->u.info); break; } case VKFLT: { luaK_codek(fs, reg, luaK_numberK(fs, e->u.nval)); break; } case VKINT: { luaK_codek(fs, reg, luaK_intK(fs, e->u.ival)); break; } case VRELOCABLE: { Instruction *pc = &getcode(fs, e); SETARG_A(*pc, reg); break; } case VNONRELOC: { if (reg != e->u.info) luaK_codeABC(fs, OP_MOVE, reg, e->u.info, 0); break; } default: { lua_assert(e->k == VVOID || e->k == VJMP); return; /* nothing to do... */ } } e->u.info = reg; e->k = VNONRELOC;}
先调用discharge2reg将表达式e安置在寄存器reg中,然后判定e是否为跳转指令,由于OPCODE=OP_NEWTABLE,且e->f=t=NO_JUMP,所以不存在跳转,进行相应的赋值后函数返回:
e->u.info = reg; e->k = VNONRELOC;
接着checknext(ls, ‘{‘):
do { lua_assert(cc.v.k == VVOID || cc.tostore > 0); if (ls->t.token == '}') break; closelistfield(fs, &cc); field(ls, &cc); } while (testnext(ls, ',') || testnext(ls, ';'));
在do-while语句中,如果读到’}’,说明constructor结束了,break
closelistfield在cc.v.k=VVOID的时候什么也没做,接下来运行到field:
static void field (LexState *ls, struct ConsControl *cc) { /* field -> listfield | recfield */ switch(ls->t.token) { case TK_NAME: { /* may be 'listfield' or 'recfield' */ if (luaX_lookahead(ls) != '=') /* expression? */ listfield(ls, cc); else recfield(ls, cc); break; } case '[': { recfield(ls, cc); break; } default: { listfield(ls, cc); break; } }}
field有三种表示:
1. {key=value}形式,例如 t = {a=1,b=2,…}
2. {[“key”]=value}形式,例如t = {[“a”]=1, [2] = 2}
3. {val1,val2,…} 形式,例如 t = {1,2,3,4}
其中case1和case2比较类似,它们最终都调用recfield来实现:
static void recfield (LexState *ls, struct ConsControl *cc) { /* recfield -> (NAME | '['exp1']') = exp1 */ FuncState *fs = ls->fs; int reg = ls->fs->freereg; expdesc key, val; int rkkey; if (ls->t.token == TK_NAME) { checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); checkname(ls, &key); } else /* ls->t.token == '[' */ yindex(ls, &key); cc->nh++; checknext(ls, '='); rkkey = luaK_exp2RK(fs, &key); expr(ls, &val); luaK_codeABC(fs, OP_SETTABLE, cc->t->u.info, rkkey, luaK_exp2RK(fs, &val)); fs->freereg = reg; /* free registers */}
先调用checkname或者yindex,为表达式key赋值,然后将表达式安置到寄存器或常量表中,skip ‘=’,接着解析expr表达式,保存结果到val。
最后调用luaK_codeABC编码,一条OP_SETTABLE指令就这样产生了,其中cc->t->u.info表示table,rkkey表示key, luaK_exp2RK(fs,&val)表示value
逻辑指令为:OP_SETTABLE table, key, value。
再看case3,调用listfield:
static void listfield (LexState *ls, struct ConsControl *cc) { /* listfield -> exp */ expr(ls, &cc->v); checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); cc->na++; cc->tostore++;}static void closelistfield (FuncState *fs, struct ConsControl *cc) { if (cc->v.k == VVOID) return; /* there is no list item */ luaK_exp2nextreg(fs, &cc->v); cc->v.k = VVOID; if (cc->tostore == LFIELDS_PER_FLUSH) { luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); /* flush */ cc->tostore = 0; /* no more items pending */ }}
listfield直接将解析的表达式保存在cc->v中,等到do-while一下次循环,调用closelistfield,将cc->v的值再保存到寄存器中。
最后调用到lastlistfield :
static void lastlistfield (FuncState *fs, struct ConsControl *cc) { if (cc->tostore == 0) return; if (hasmultret(cc->v.k)) { luaK_setmultret(fs, &cc->v); luaK_setlist(fs, cc->t->u.info, cc->na, LUA_MULTRET); cc->na--; /* do not count last expression (unknown number of elements) */ } else { if (cc->v.k != VVOID) luaK_exp2nextreg(fs, &cc->v); luaK_setlist(fs, cc->t->u.info, cc->na, cc->tostore); }}
查看 if (hasmultret(cc->v.k)) 语句的else分支,发现,接着又调用了luaK_exp2nextreg,这等于变相的调用了一次closelistfield,目的是为了对最后一个list元素进行处理,接着调用luaK_setlist进行编码。
回头再看constructor函数实现的最后两行:
SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */
上面这段代码用来设定OP_NEWTABLE指令的两个参数,一个表示数组大小,一个表示hash表大小。
到这里,table constructor讲解完毕。
2.1.2 suffixedexp
请看该函数的定义:
/* * 解析带有后缀的表达式,分以下几种形式: * 1. exp.a.b... 使用域运算符 * 2. exp[a][b]... 使用table键值引用 * 3. exp:a(...) 使用成员函数调用 * 4. exp(...) 直接函数调用 */static void suffixedexp (LexState *ls, expdesc *v) { /* suffixedexp -> primaryexp { '.' NAME | '[' exp ']' | ':' NAME funcargs | funcargs } */ FuncState *fs = ls->fs; int line = ls->linenumber; primaryexp(ls, v); for (;;) { switch (ls->t.token) { case '.': { /* fieldsel */ fieldsel(ls, v); break; } case '[': { /* '[' exp1 ']' */ expdesc key; luaK_exp2anyregup(fs, v); yindex(ls, &key); luaK_indexed(fs, v, &key); break; } case ':': { /* ':' NAME funcargs */ expdesc key; luaX_next(ls); checkname(ls, &key); luaK_self(fs, v, &key); funcargs(ls, v, line); break; } case '(': case TK_STRING: case '{': { /* funcargs */ luaK_exp2nextreg(fs, v); funcargs(ls, v, line); break; } default: return; } }}
如果是’.’符号,调用fieldsel:
static void fieldsel (LexState *ls, expdesc *v) { /* fieldsel -> ['.' | ':'] NAME */ FuncState *fs = ls->fs; expdesc key; luaK_exp2anyregup(fs, v); luaX_next(ls); /* skip the dot or colon */ checkname(ls, &key); luaK_indexed(fs, v, &key);}
先调用luaK_exp2anyregup将其转到寄存器中,然后读取’.’后面的key,编码成indexed。
luaK_indexed定义如下:
void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { lua_assert(!hasjumps(t)); t->u.ind.t = t->u.info; t->u.ind.idx = luaK_exp2RK(fs, k); t->u.ind.vt = (t->k == VUPVAL) ? VUPVAL : check_exp(vkisinreg(t->k), VLOCAL); t->k = VINDEXED;}
可以看到,t->u.ind里面的字段是专门为VINDEXED使用的,包括table, key, type(VUPVAL, VLOCAL)。
注意,最后将t->k设置为VINDEXED了。
读取’:’,之后后来接着读name,然后编码luaK_self:
/** R(A+1) = R(B)* R(A) = R(B)[Rk(C)]*/void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { int ereg; luaK_exp2anyreg(fs, e); ereg = e->u.info; /* register where 'e' was placed */ freeexp(fs, e); e->u.info = fs->freereg; /* base register for op_self */ e->k = VNONRELOC; luaK_reserveregs(fs, 2); /* function and 'self' produced by op_self */ luaK_codeABC(fs, OP_SELF, e->u.info, ereg, luaK_exp2RK(fs, key)); freeexp(fs, key);}
函数注释表示的就是OP_SELF完成的功能。
注意:luaK_reserveregs(fs,2)保留两个寄存器,一个是给R(A)用,一个是给R(A+1)用。
无论如何,后面都会调用到funcargs函数:
static void funcargs (LexState *ls, expdesc *f, int line) { FuncState *fs = ls->fs; expdesc args; int base, nparams; switch (ls->t.token) { case '(': { /* funcargs -> '(' [ explist ] ')' */ luaX_next(ls); if (ls->t.token == ')') /* arg list is empty? */ args.k = VVOID; else { explist(ls, &args); luaK_setmultret(fs, &args); } check_match(ls, ')', '(', line); break; } case '{': { /* funcargs -> constructor */ constructor(ls, &args); break; } case TK_STRING: { /* funcargs -> STRING */ codestring(ls, &args, ls->t.seminfo.ts); luaX_next(ls); /* must use 'seminfo' before 'next' */ break; } default: { luaX_syntaxerror(ls, "function arguments expected"); } } lua_assert(f->k == VNONRELOC); base = f->u.info; /* base register for call */ if (hasmultret(args.k)) nparams = LUA_MULTRET; /* open call */ else { if (args.k != VVOID) luaK_exp2nextreg(fs, &args); /* close last argument */ nparams = fs->freereg - (base+1); } init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); luaK_fixline(fs, line); /* */ fs->freereg = base+1; /* call remove function and arguments and leaves (unless changed) one result */}
里面会读到表达式列表,可以看到表达式列表中的每一个表达式都被放在寄存器中,。。。最后编码成
OP_CALL,表明它是一个函数调用。
2.1.3 test_then_block
看一下这个函数test_then_block:
/* * 解析 if-then 语句 */static void test_then_block (LexState *ls, int *escapelist) { /* test_then_block -> [IF | ELSEIF] cond THEN block */ BlockCnt bl; FuncState *fs = ls->fs; expdesc v; int jf; /* instruction to skip 'then' code (if condition is false) */ luaX_next(ls); /* skip IF or ELSEIF */ expr(ls, &v); /* read condition */ checknext(ls, TK_THEN); if (ls->t.token == TK_GOTO || ls->t.token == TK_BREAK) { luaK_goiffalse(ls->fs, &v); /* will jump to label if condition is true */ enterblock(fs, &bl, 0); /* must enter block before 'goto' */ gotostat(ls, v.t); /* handle goto/break */ skipnoopstat(ls); /* skip other no-op statements */ if (block_follow(ls, 0)) { /* 'goto' is the entire block? */ leaveblock(fs); return; /* and that is it */ } else /* must skip over 'then' part if condition is false */ jf = luaK_jump(fs); } else { /* regular case (not goto/break) */ luaK_goiftrue(ls->fs, &v); /* skip over block if condition is false */ enterblock(fs, &bl, 0); /* 设定if判定为false时需要执行的JMP指令(所对应的pc)*/ jf = v.f; } statlist(ls); /* 'then' part */ leaveblock(fs); if (ls->t.token == TK_ELSE || ls->t.token == TK_ELSEIF) /* followed by 'else'/'elseif'? */ /* 如果有else或者elseif则需要insert JMP指令,该JMP指令用于跳出接下来的语句 */ luaK_concat(fs, escapelist, luaK_jump(fs)); /* must jump over it */ luaK_patchtohere(fs, jf);}
解析完if语句里面的表达式之后,假定下一条语句不是goto,则进入regular case,接着调用luaK_goiftrue:
/* * 对表达式e进行判定,若为true则pc++;如果为fales则需要执行JMP */void luaK_goiftrue (FuncState *fs, expdesc *e) { int pc; /* pc of last jump */ luaK_dischargevars(fs, e); switch (e->k) { case VJMP: { invertjump(fs, e); pc = e->u.info; break; } case VK: case VKFLT: case VKINT: case VTRUE: { pc = NO_JUMP; /* always true; do nothing */ break; } default: { pc = jumponcond(fs, e, 0); break; } } luaK_concat(fs, &e->f, pc); /* insert last jump in 'f' list */ luaK_patchtohere(fs, e->t); e->t = NO_JUMP;}
由于if的判定表达式一般为VJMP,则调用invertjump(这个实际上跟lvm.c里面解析OPTEST命令相关),标明为false的话则需要跳转,然后还将e->f和pc concat起来。如果if的判定式必定为TRUE的话,则pc=NO_JUMP,表示不可能跳转了。
调用luaK_goiftrue返回,接着解析if语句里面的statelist,解析完之后如果紧跟着else或者else-if,则马上生产一条JMP指令,用于escape整条if-{else-if|else}-end 语句。
一直循环下去直至遇到else-end语句。
回头再来看ifstat函数:
static void ifstat (LexState *ls, int line) { /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ FuncState *fs = ls->fs; int escapelist = NO_JUMP; /* exit list for finished parts */ test_then_block(ls, &escapelist); /* IF cond THEN block */ while (ls->t.token == TK_ELSEIF) test_then_block(ls, &escapelist); /* ELSEIF cond THEN block */ if (testnext(ls, TK_ELSE)) block(ls); /* 'else' part */ check_match(ls, TK_END, TK_IF, line); luaK_patchtohere(fs, escapelist); /* patch escape list to 'if' end */}
调用luaK_patchtohere函数:将escapelist这条指向JMP指令“hold住”,然后让这条JMP指令可以直接跳转到下一条指令:
/* * 设置fs->jpc到list * 以便在对下一条语句进行编码时,将pc[list]的JMP目标设置为下一条语句 */void luaK_patchtohere (FuncState *fs, int list) { luaK_getlabel(fs); luaK_concat(fs, &fs->jpc, list);}
可以看见,它是直接通过修改jpc来实现的,然后在下一条指令编码的时候必定调用该函数:
static int luaK_code (FuncState *fs, Instruction i) { Proto *f = fs->f; dischargejpc(fs); /* 'pc' will change */ /* put new instruction in code array */ luaM_growvector(fs->ls->L, f->code, fs->pc, f->sizecode, Instruction, MAX_INT, "opcodes"); f->code[fs->pc] = i; /* save corresponding line information */ luaM_growvector(fs->ls->L, f->lineinfo, fs->pc, f->sizelineinfo, int, MAX_INT, "opcodes"); f->lineinfo[fs->pc] = fs->ls->lastline; return fs->pc++;}static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, int dtarget) { while (list != NO_JUMP) { int next = getjump(fs, list); if (patchtestreg(fs, list, reg)) fixjump(fs, list, vtarget); else fixjump(fs, list, dtarget); /* jump to default target */ list = next; }}/* * 令jpc JMP 到当前 fs->pc * 注意: jpc由luaK_patchtohere函数来修改 */static void dischargejpc (FuncState *fs) { patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); fs->jpc = NO_JUMP;}
调用dischargejpc,就是使jpc可以直接跳转到当前编码的这条指令。
假定if语句是这样子的:
if a > b then
max = a
else
max = b
end
max = max*2
0: LT 0,b,a
1: JMP 4
2: MOV max, a
3: JMP 5
4: MOV max b
5: MUL max, 2
其中1:JMP 4 表示jump to false,而3:JMP 5表示jump to escape。
未完待续… …
- Lua源码解析之三:code
- Lua源码解析之二:parser
- protobuf lua源码解析
- lua源码剖析(三)
- Spring 源码解析之HandlerAdapter源码解析(三)
- 【JDk源码解析之三】HashMap源码解析
- Spring 源码解析之HandlerAdapter源码解析(三)
- Spring 源码解析之HandlerAdapter源码解析(三)
- 音乐播放器之源码解析三
- Android入门笔记之源码解析三
- (三)Mina源码解析之IoFilter
- (三)MyBatis源码解析之配置文件
- Spring源码之Resource加载源码解析(三)
- 闲扯LUA之三
- AFNetworking源码解析<三>
- TFS源码解析三
- AFNetworking源码解析<三>
- AFNetworking源码解析<三>
- 从Android/Java基础之上学习C/C++语言(3)--C语言基础--内存分配
- Android中为什么主线程不会因为Looper.loop()里的死循环卡死
- [LeetCode]227. Basic Calculator II
- postfix路由流程
- visual studio 安装教程
- Lua源码解析之三:code
- Python优雅的合并两个Dict
- 标准模板库STL学习总结
- 關於 python中的selenium 錯誤
- http
- cin.get()和cin.peek()
- HDOJ1408 简单模拟+浮点数精度
- PLSQL根据页码及每页大小获取数据
- 欢迎使用CSDN-markdown编辑器