首页 > 代码库 > 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 虚拟机指令分析(二)