首页 > 代码库 > Lua1.1 虚拟机指令分析(二)

Lua1.1 虚拟机指令分析(二)

(接上篇)
> EQOP

   case EQOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    --top;
    if (tag(l) != tag(r))
     tag(top-1) = T_NIL;
    else
    {
     switch (tag(l))
     {
      case T_NIL: tag(top-1) = T_NUMBER; break;
      case T_NUMBER: tag(top-1) = (nvalue(l) == nvalue(r)) ? T_NUMBER : T_NIL; break;
      case T_ARRAY: tag(top-1) = (avalue(l) == avalue(r)) ? T_NUMBER : T_NIL; break;
      case T_FUNCTION: tag(top-1) = (bvalue(l) == bvalue(r)) ? T_NUMBER : T_NIL; break;
      case T_CFUNCTION: tag(top-1) = (fvalue(l) == fvalue(r)) ? T_NUMBER : T_NIL; break;
      case T_USERDATA: tag(top-1) = (uvalue(l) == uvalue(r)) ? T_NUMBER : T_NIL; break;
      case T_STRING: tag(top-1) = (strcmp (svalue(l), svalue(r)) == 0) ? T_NUMBER : T_NIL; break;
      case T_MARK: return 1;
     }
    }
    nvalue(top-1) = 1;
   }
   break;

  判断栈顶的两个 Object 是否相等。
  先判断是否是相同类型,如果不是,则不等。
  否则,判断值是否相等。数值和字符串类型是判断是否值相等,而数组、函数这种则是判断其指针是否相等。
  如果相等,把栈顶 Object 设置为 T_NUMBER 类型,值设置为 1。
  如果不等,设置栈顶 Object 为 T_NIL(空类型足以识别它自己,此时可以认为设置什么值都无意义)。

> LTOP

   case LTOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    --top;
    if (tag(l) == T_NUMBER && tag(r) == T_NUMBER)
     tag(top-1) = (nvalue(l) < nvalue(r)) ? T_NUMBER : T_NIL;
    else
    {
     if (tostring(l) || tostring(r))
      return 1;
     tag(top-1) = (strcmp (svalue(l), svalue(r)) < 0) ? T_NUMBER : T_NIL;
    }
    nvalue(top-1) = 1;
   }
   break;

  判断 top-2 是否小于 top-1。只在两个 Object 均为数值型,或者可转化为字符串型时有意义。
  如果判断结果为真,设置栈顶元素类型为 T_NUMBER,值为 1。
  如果判断结果为假,设置栈顶元素类型为 T_NIL。

> LEOP

   case LEOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    --top;
    if (tag(l) == T_NUMBER && tag(r) == T_NUMBER)
     tag(top-1) = (nvalue(l) <= nvalue(r)) ? T_NUMBER : T_NIL;
    else
    {
     if (tostring(l) || tostring(r))
      return 1;
     tag(top-1) = (strcmp (svalue(l), svalue(r)) <= 0) ? T_NUMBER : T_NIL;
    }
    nvalue(top-1) = 1;
   }
   break;

  判断 top-2 是否小于等于 top-1。只在两个 Object 均为数值型,或者可转化为字符串型时有意义。
  如果判断结果为真,设置栈顶元素类型为 T_NUMBER,值为 1。
  如果判断结果为假,设置栈顶元素类型为 T_NIL。

> ADDOP

   case ADDOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    if (tonumber(r) || tonumber(l))
     return 1;
    nvalue(l) += nvalue(r);
    --top;
   }
   break;

  栈顶两元素 top-2 和 top-1 相加,top-1 出栈,结果保存在新的栈顶。
  只在两个 Object 均可转化为数值型时该运算有效。

> SUBOP,

   case SUBOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    if (tonumber(r) || tonumber(l))
     return 1;
    nvalue(l) -= nvalue(r);
    --top;
   }
   break;

  栈顶两元素 top-2 和 top-1 相减,top-1 出栈,结果保存在新的栈顶。
  只在两个 Object 均可转化为数值型时该运算有效。

> MULTOP,

   case MULTOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    if (tonumber(r) || tonumber(l))
     return 1;
    nvalue(l) *= nvalue(r);
    --top;
   }
   break;

  栈顶两元素 top-2 和 top-1 相乘,top-1 出栈,结果保存在新的栈顶。
  只在两个 Object 均可转化为数值型时该运算有效。

> DIVOP,

   case DIVOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    if (tonumber(r) || tonumber(l))
     return 1;
    nvalue(l) /= nvalue(r);
    --top;
   }
   break;

  栈顶两元素 top-2 和 top-1 相除,top-1 出栈,结果保存在新的栈顶。
  只在两个 Object 均可转化为数值型时该运算有效。

> CONCOP,

   case CONCOP:
   {
    Object *l = top-2;
    Object *r = top-1;
    if (tostring(r) || tostring(l))
     return 1;
    svalue(l) = lua_createstring (lua_strconc(svalue(l),svalue(r)));
    if (svalue(l) == NULL)
     return 1;
    --top;
   }
   break;

  栈顶两元素 top-2 和 top-1 字符串连接,top-1 出栈,结果保存在新的栈顶。
  只在两个 Object 均可转化为字符串型时该运算有效。
  连接结果是新生成的字符串,原来的两个字符串保持不变。

> MINUSOP

   case MINUSOP:
    if (tonumber(top-1))
     return 1;
    nvalue(top-1) = - nvalue(top-1);
   break;

  栈顶元素数值取反,只在其可转化为数值型时有效。

> NOTOP

   case NOTOP:
    tag(top-1) = tag(top-1) == T_NIL ? T_NUMBER : T_NIL;
   break;

  栈顶元素逻辑取反。
  当栈顶元素为假(T_NIL)时设置其为真(T_NUMBER)。
  当栈顶元素为真(非 T_NIL 型)时设置其为假(T_NIL)。

> ONTJMP

   case ONTJMP:
   {
    CodeWord code;
    get_word(code,pc);
    if (tag(top-1) != T_NIL) pc += code.w;
   }
   break;

  真跳转,如果判断栈顶元素为真则跳转,指令偏移量从字节码中取得。

> ONFJMP

   case ONFJMP:
   {
    CodeWord code;
    get_word(code,pc);
    if (tag(top-1) == T_NIL) pc += code.w;
   }
   break;

  假跳转,如果判断栈顶元素为假则跳转,指令偏移量从字节码中取得。

> JMP

   case JMP:
   {
    CodeWord code;
    get_word(code,pc);
    pc += code.w;
   }
   break;

  无条件跳转,指令偏移量从字节码中取得。

> UPJMP

   case UPJMP:
   {
    CodeWord code;
    get_word(code,pc);
    pc -= code.w;
   }
   break;

  向上无条件跳转,指令偏移量从字节码中取得。


> IFFJMP

   case IFFJMP:
   {
    CodeWord code;
    get_word(code,pc);
    top--;
    if (tag(top) == T_NIL) pc += code.w;
   }
   break;

  假跳转,如果判断栈顶元素为假则跳转,指令偏移量从字节码中取得。
  判断条件出栈。

> IFFUPJMP

   case IFFUPJMP:
   {
    CodeWord code;
    get_word(code,pc);
    top--;
    if (tag(top) == T_NIL) pc -= code.w;
   }
   break;

  假跳转,如果判断栈顶元素为假则向上跳转,指令偏移量从字节码中取得。
  判断条件出栈。

> POP

   case POP: --top; break;

  出栈。

> CALLFUNC

   case CALLFUNC:
   {
    Byte *newpc;
    Object *b = top-1;
    while (tag(b) != T_MARK) b--;
    if (tag(b-1) == T_FUNCTION)
    {
     lua_debugline = 0;     /* always reset debug flag */
     newpc = bvalue(b-1);
     bvalue(b-1) = pc;     /* store return code */
     nvalue(b) = (base-stack);     /* store base value */
     base = b+1;
     pc = newpc;
     if (MAXSTACK-(base-stack) < STACKGAP)
     {
      lua_error ("stack overflow");
      return 1;
     }
    }
    else if (tag(b-1) == T_CFUNCTION)
    {
     int nparam;
     lua_debugline = 0;     /* always reset debug flag */
     nvalue(b) = (base-stack);     /* store base value */
     base = b+1;
     nparam = top-base;     /* number of parameters */
     (fvalue(b-1))();     /* call C function */
     
     /* shift returned values */
     {
      int i;
      int nretval = top - base - nparam;
      top = base - 2;
      base = stack + (int) nvalue(base-1);
      for (i=0; i<nretval; i++)
      {
       *top = *(top+nparam+2);
       ++top;
      }
     }
    }
    else
    {
     lua_reportbug ("call expression not a function");
     return 1;
    }
   }
   break;

  函数调用,从栈顶到栈中类型为 T_MARK 的 Object 均为函数参数。
  类型为 T_MARK 的 Object 的下一个 Object 为函数名。
  函数调用有两种:
  一种为 Lua 脚本中定义的函数的调用,相应的 Object 类型为 T_FUNCTION。
  另一种为 Lua 脚本中调用 C 写的函数,相应的 Object 类型为 T_CFUNCTION。
  T_FUNCTION 调用前保存当前执行字节码 pc 和栈的 base 以做调用结束后的恢复,设置 pc 为调用函数的字节码地址即可。
  T_CFUNCTION 调用前保存栈的 base 以做调用结束后的恢复,取得函数参数个数。
  函数调用结束后,调整返回值的位置,恢复 top 和 base 的值。把返回值调整得和 Lua 函数返回的一样。
  也就是原来函数 Object 和 T_MARK 的地方被返回值替掉。
  可以看出来,这里的函数调用和汇编或者 C 语言里的函数调用的栈桢是长得很像的(除了没有寄存器)。

> RETCODE

   case RETCODE:
   {
    int i;
    int shift = *pc++;
    int nretval = top - base - shift;
    top = base - 2;
    pc = bvalue(base-2);
    base = stack + (int) nvalue(base-1);
    for (i=0; i<nretval; i++)
    {
     *top = *(top+shift+2);
     ++top;
    }
   }
   break;

  拷贝返回值到函数调用前的位置,就是栈里 T_MARK 前面的那个函数类型的 Object 处。
  当前的字节码保存的偏移量,通过计算 top, base, shift 之差得到返回值个数。 shift 是指函数有几个参数。
  重置 top, pc, base。
  把函数的返回值拷贝到栈上原来的函数 Object 处。

> HALT

   case HALT:
    base = oldbase;
   return 0;     /* success */

  正常停止,重置栈。

> SETFUNCTION

   case SETFUNCTION:
   {
    CodeWord file, func;
    get_word(file,pc);
    get_word(func,pc);
    if (lua_pushfunction (file.w, func.w))
     return 1;
   }
   break;

  设置函数,函数的文件名和函数地址从字节码里取得。

> SETLINE

   case SETLINE:
   {
    CodeWord code;
    get_word(code,pc);
    lua_debugline = code.w;
   }
   break;

  设置 debug 行号。

> RESET

   case RESET:
    lua_popfunction ();
   break;

  弹出函数

Lua1.1 虚拟机指令分析(二)