0x0. 原理


控制结构包括条件控制,循环控制。不管是哪一种都可以简化为两部分:

  • 语句块:顺序执行的语句组合。
  • 跳转:包括循环的条件控制,if/else控制,break。 ```lua while cond do —do something end

— 简化后:

test cond jump after-end block

after-end

  1. ```lua
  2. if cond
  3. then
  4. --block1
  5. else
  6. --block2
  7. end
  8. --
  9. 简化后
  10. test cond
  11. jump block2
  12. block1
  13. jump after-end
  14. block2
  15. jump test
  16. after-end

语句块

static void block (LexState *ls) {
    /* block -> chunk */
    FuncState *fs = ls->fs;
    BlockCnt bl;
    enterblock(fs, &bl, 0);
    chunk(ls);
    lua_assert(bl.breaklist == NO_JUMP);
    leaveblock(fs);
}

跳转

从上面简化后的跳转可以看出有个问题,就是我们在生成跳转语句的时候,其实我们是不知道要跳转到哪里的。因为要跳转的地方是在跳转语句之后。一种很容易想到的做法就是我们把跳转语句都保存下来,等待block都解析完成再去填充跳转语句的值。lua的实现思路也是类似,不过这里的实现比较巧妙,它把各个跳转语句链起来了。如下图所示。
image.png

//将一个跳转语句加入到跳转链表中
void luaK_concat (FuncState *fs, int *l1, int l2) {
  if (l2 == NO_JUMP) return;
  else if (*l1 == NO_JUMP)
    *l1 = l2;
  else {
    int list = *l1;
    int next;
    while ((next = getjump(fs, list)) != NO_JUMP)  /* find last element */
      list = next;
    fixjump(fs, list, l2);
  }
}

//将跳转链表加入到fs->jpc中,生成下一条指令的时候就会去解析jpc链表,将跳转指令修改为正确的值。
//在一个block结束的时候就会调用这个
void luaK_patchtohere (FuncState *fs, int list) {
  luaK_getlabel(fs);
  luaK_concat(fs, &fs->jpc, list);
}

0x2. while语句


static void whilestat (LexState *ls, int line) {
  /* whilestat -> WHILE cond DO block END */
  FuncState *fs = ls->fs;
  int whileinit;
  int condexit;
  BlockCnt bl;
  luaX_next(ls);  /* skip WHILE */
  whileinit = luaK_getlabel(fs);//缓存while的第一条语句
  condexit = cond(ls);
  enterblock(fs, &bl, 1);
  checknext(ls, TK_DO);
  block(ls);
  luaK_patchlist(fs, luaK_jump(fs), whileinit);//生成跳转语句到while之前的语句
  check_match(ls, TK_END, TK_WHILE, line);
  leaveblock(fs); //这里面会处理breaklist,也就是通过break跳出的jump语句
  luaK_patchtohere(fs, condexit);  //将条件判断语句的jump重置为下一条语句
}

0x3. ifelse


static void ifstat (LexState *ls, int line) {
  /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */
  FuncState *fs = ls->fs;
  int flist;
  int escapelist = NO_JUMP;
  flist = test_then_block(ls);  /* flist即为if的跳转语句*/
  while (ls->t.token == TK_ELSEIF) {
    luaK_concat(fs, &escapelist, luaK_jump(fs));
    luaK_patchtohere(fs, flist);/*循环将flist更新为elseif语句*/
    flist = test_then_block(ls);  /* ELSEIF cond THEN block */
  }
  if (ls->t.token == TK_ELSE) {
    luaK_concat(fs, &escapelist, luaK_jump(fs));
    luaK_patchtohere(fs, flist);
    luaX_next(ls);  /* skip ELSE (after patch, for correct line info) */
    block(ls);  /* `else' part */
  }
  else
    luaK_concat(fs, &escapelist, flist);
  luaK_patchtohere(fs, escapelist);/*escapelist即为各个block执行完要跳转的链表*/
  check_match(ls, TK_END, TK_IF, line);
}