Lua源码解析之三:code

本文深入探讨Lua源码,解析Proto结构体中的code字段,阐述如何将语句转换为指令。文章以if语句为例,详细解释了luaK_codeABC等指令编码过程,以及table constructor、suffixedexp和test_then_block的处理方法。
摘要由CSDN通过智能技术生成

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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值