Lua5.3 VM 分析(四)分支和跳转
来源:互联网 发布:已备案域名已删除 编辑:程序博客网 时间:2024/05/29 09:07
Lua5.3 VM 分析(四)分支和跳转
Lua VM 定义了 OP_EQ、OP_LT、OP_LE、OP_TEST、OP_TESTSET 五种分支操作。
这五个分支指令必须与 之后的 跳转指令 JMP 看做一个整体解释。也就是说:当条件成立时,继续运行;条件不成立时,跳转到指定位置。
如果条件成立跳转到L1, 否则跳转到L2: L1: success() jmp exit L2: fail() jmp exit exit: exit()
OP_JMP
OP_JMP 可以单独使用, 做无条件跳转指令。跳转地址使用的是相对量,负数表示向前跳转。0 表示下一条指令。
vmcase(OP_JMP) { dojump(ci, i, 0); vmbreak; }define dojump(ci,i,e) \ { int a = GETARG_A(i); \if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \ci->u.l.savedpc += GETARG_sBx(i) + e; }define donextjump(ci) { i = *ci->u.l.savedpc; dojump(ci, i, 1); }
无条件跳转和条件跳转分别用 dojump 宏 与 donextjump 两个宏实现。
donextjump 读出下一条指令,必定是JMP。
dojump 宏负责 指令的偏移 ( ci->u.l.savedpc += GETARG_sBx(i) + e; )。
当 a (Instruction 的 A 部分)大于 0 时,调用luaF_close 函数关闭 A 层次的 upvalue 。
OP_EQ
vmcase(OP_EQ) {TValue *rb = RKB(i);TValue *rc = RKC(i);Protect( if (cast_int(luaV_equalobj(L, rb, rc)) != GETARG_A(i))ci->u.l.savedpc++; elsedonextjump(ci);)vmbreak; }
利用 luaV_equalobj 函数判断条件是否成立,当不成立时,执行 donextjump 。反则根据 类型不同比较算法不同。
int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) { const TValue *tm; if (ttype(t1) != ttype(t2)) { /* not the same variant? */if (ttnov(t1) != ttnov(t2) || ttnov(t1) != LUA_TNUMBER) return 0; /* only numbers can be equal with different variants */else { /* two numbers with different variants */ lua_Number n1, n2; /* compare them as floats */ lua_assert(ttisnumber(t1) && ttisnumber(t2)); cast_void(tofloat(t1, &n1)); cast_void(tofloat(t2, &n2)); return luai_numeq(n1, n2);} } /* values have same type and same variant */ switch (ttype(t1)) {case LUA_TNIL: return 1;case LUA_TNUMINT: return (ivalue(t1) == ivalue(t2));case LUA_TNUMFLT: return luai_numeq(fltvalue(t1), fltvalue(t2));case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);case LUA_TLCF: return fvalue(t1) == fvalue(t2);case LUA_TSHRSTR: return eqshrstr(tsvalue(t1), tsvalue(t2));case LUA_TLNGSTR: return luaS_eqlngstr(tsvalue(t1), tsvalue(t2));case LUA_TUSERDATA: { if (uvalue(t1) == uvalue(t2)) return 1; else if (L == NULL) return 0; tm = fasttm(L, uvalue(t1)->metatable, TM_EQ); if (tm == NULL)tm = fasttm(L, uvalue(t2)->metatable, TM_EQ); break; /* will try TM */}case LUA_TTABLE: { if (hvalue(t1) == hvalue(t2)) return 1; else if (L == NULL) return 0; tm = fasttm(L, hvalue(t1)->metatable, TM_EQ); if (tm == NULL)tm = fasttm(L, hvalue(t2)->metatable, TM_EQ); break; /* will try TM */}default: return gcvalue(t1) == gcvalue(t2); } if (tm == NULL) /* no TM? */return 0; /* objects are different */ luaT_callTM(L, tm, t1, t2, L->top, 1); /* call TM */ return !l_isfalse(L->top);}
Lua 的不同类型比较操作分开处理。
其中 LUA_TNIL、LUA_TNUMINT 、LUA_TNUMFLT、LUA_TBOOLEAN、LUA_TLIGHTUSERDATA、LUA_TLCF、LUA_TUSERDATA 都是值类型,只需要值比较。O(1) 操作。
LUA_TSHRSTR 短字符串会做字符串内部化,所以只需要直接比较指针。
define eqshrstr(a,b) check_exp((a)->tt == LUA_TSHRSTR, (a) == (b))
LUA_TLNGSTR 长字符串可能会触发完整的比较:
int luaS_eqlngstr (TString *a, TString *b) { size_t len = a->len; lua_assert(a->tt == LUA_TLNGSTR && b->tt == LUA_TLNGSTR); return (a == b) || /* same instance or... */((len == b->len) && /* equal length and ... */ (memcmp(getstr(a), getstr(b), len) == 0)); /* equal contents */}
OP_LT OP_LE
OP_LT 操作由 luaV_lessthan 函数完成,OP_LE 操作由luaV_lessequal 函数完成。大于操作可以由小于等于 取反得到。
luaV_lessthan 函数逻辑:
1. 如果两个参数都是 integer 则利用ivalue 转换类型 直接比较。
2. 如果两个参数都是 float 则利用luai_numlt 比较。
3. 如果两个参数都是 字符串,则调用l_strcmp 辅助函数比较。
4. 触发LT 元方法
luaV_ lessequal 函数逻辑:
1. 如果两个参数都是 integer 则利用ivalue 转换类型 直接比较。
2. 如果两个参数都是 float 则利用luai_numlt 比较。
3. 如果两个参数都是 字符串,则调用l_strcmp 辅助函数比较。
4. 尝试触发LE 元方法
5. 尝试触发LT 元方法
vmcase(OP\_LT) { Protect( if (luaV\_lessthan(L, RKB(i), RKC(i)) != GETARG\_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) vmbreak; } vmcase(OP\_LE) { Protect( if (luaV\_lessequal(L, RKB(i), RKC(i)) != GETARG\_A(i)) ci->u.l.savedpc++; else donextjump(ci); ) vmbreak; } int luaV\_lessthan (lua\_State *L, const TValue *l, const TValue \*r) { int res; lua\_Number nl, nr; if (ttisinteger(l) && ttisinteger(r)) /\* both operands are integers? \*/ return (ivalue(l) < ivalue(r)); else if (tofloat(l, &nl) && tofloat(r, &nr)) /\* both are numbers? \*/ return luai\_numlt(nl, nr); else if (ttisstring(l) && ttisstring(r)) /\* both are strings? \*/ return l\_strcmp(tsvalue(l), tsvalue(r)) < 0; else if ((res = luaT\_callorderTM(L, l, r, TM\_LT)) < 0) /\* no metamethod? \*/ luaG\_ordererror(L, l, r); /\* error \*/ return res; } int luaV\_lessequal (lua\_State *L, const TValue *l, const TValue \*r) { int res; lua\_Number nl, nr; if (ttisinteger(l) && ttisinteger(r)) /\* both operands are integers? \*/ return (ivalue(l) <= ivalue(r)); else if (tofloat(l, &nl) && tofloat(r, &nr)) /\* both are numbers? \*/ return luai\_numle(nl, nr); else if (ttisstring(l) && ttisstring(r)) /\* both are strings? \*/ return l\_strcmp(tsvalue(l), tsvalue(r)) <= 0; else if ((res = luaT\_callorderTM(L, l, r, TM\_LE)) >= 0) /\* first try 'le' \*/ return res; else if ((res = luaT\_callorderTM(L, r, l, TM\_LT)) < 0) /\* else try 'lt' \*/ luaG\_ordererror(L, l, r); return !res; }
字符串比较
Lua 字符串比较并不是简单的调用 C 标准库函函数 memcmp 比较的,而是考虑了系统locale 定义,利用 C 标准库函数 strcoll 来实现。
Lua 的字符串与 C字符串不同,不以’\0’做结束标志,也就是说 在Lua TString
中可能包含 ‘\0’。一次strcoll 调用并不一定得到结果。每次strcoll 比较完,如果字符串相等则要跳过’\0’继续比较,直到 达到TString 中记录的字符串的长度为止。
static int l_strcmp (const TString *ls, const TString *rs) { const char *l = getstr(ls); size_t ll = ls->len; const char *r = getstr(rs); size_t lr = rs->len; for (;;) { /* for each segment */int temp = strcoll(l, r);if (temp != 0) /* not equal? */ return temp; /* done */else { /* strings are equal up to a '\0' */ size_t len = strlen(l); /* index of first '\0' in both strings */ if (len == lr) /* 'rs' is finished? */return (len == ll) ? 0 : 1; /* check 'ls' */ else if (len == ll) /* 'ls' is finished? */return -1; /* 'ls' is smaller than 'rs' ('rs' is not finished) */ /* both strings longer than 'len'; go on comparing after the '\0' */ len++; l += len; ll -= len; r += len; lr -= len;} }}
- Lua5.3 VM 分析(四)分支和跳转
- Lua5.3 虚拟机指令分析(四)分支与跳转
- Lua5.3 VM 分析(一)字节码运行
- Lua5.3 VM 分析(二)表处理
- Lua5.3 VM 分析(三)表达式运算
- Lua5.3 VM 分析(七)生成闭包
- Lua5.3 VM 分析(八)For 循环
- C语言编程(练习3:分支和跳转 )
- 分支和跳转
- Lua5.3 虚拟机指令分析(一)概述
- Lua5.3 虚拟机指令分析(二)赋值指令
- Lua5.3 虚拟机指令分析(三)表达式运算
- Lua5.3 虚拟机指令分析(五)函数调用
- Lua5.3 虚拟机指令分析(六)不定参数
- Lua5.3 虚拟机指令分析(八)循环
- Lua5.3 虚拟机指令分析(十)表相关指令
- C语言编程(练习1:分支和跳转 )
- C语言编程(练习2:分支和跳转 )
- 打扫房间的各种方法 —— Java虚拟机的垃圾收集算法清单
- 1037. 在霍格沃茨找零钱(20)
- 如何更好的做计划-SMART原则
- Lua5.3 VM 分析(三)表达式运算
- 【数学基础】【欧拉函数解析模板】【欧拉筛法实现求1~n】【求单个n】
- Lua5.3 VM 分析(四)分支和跳转
- Lua5.3 VM 分析(七)生成闭包
- Lua5.3 VM 分析(八)For 循环
- linearlayout之margin和peddling
- talk is expensive , Don't show me your code at that time
- Linux 下如何查看cpu信息
- swift3自定义UIBotton(带动画效果)
- Activity 小结
- var_dump和var_export