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。

未完待续… …

原创粉丝点击