在語法分析 lua_parse 以後,調用 lua_execute 來執行語法分析生成的字節碼。
虛擬機的指令是一個枚舉型,就是在 opcode.h 中的 OpCode, 經過 lua_execute 中的那個 switch case 來看下指令對應的操做。
> PUSHNIL
c++
case PUSHNIL: tag(top++) = T_NIL; break;
設置棧頂的 Object 類型爲 T_NIL,這個棧就是以前所說的那個 Lua 和 C 之間交互的那個棧。
> PUSH0, PUSH1, PUSH2
express
case PUSH0: tag(top) = T_NUMBER; nvalue(top++) = 0; break; case PUSH1: tag(top) = T_NUMBER; nvalue(top++) = 1; break; case PUSH2: tag(top) = T_NUMBER; nvalue(top++) = 2; break;
設置棧頂的 Object 類型爲 T_NUMBER,值爲 0/1/2。 這個是指令優化,把操做數放到指令裏,從而讓指令更短,執行的更快些。
這幾個指令是 PUSHBYTE 指令的優化版,或者叫特化版更合適一些。
> PUSHBYTE
數組
case PUSHBYTE: tag(top) = T_NUMBER; nvalue(top++) = *pc++; break;
設置棧頂的 Object 類型爲 T_NUMBER,值從當前字節碼處(也就是 pc 指針處的一個字節)取得。值在字節碼中佔一個字節。
> PUSHWORD
函數
case PUSHWORD: { CodeWord code; get_word(code,pc); tag(top) = T_NUMBER; nvalue(top++) = code.w; } break;
設置棧頂的 Object 類型爲 T_NUMBER,值從當前字節碼處(也就是 pc 指針處的兩個字節)取得。值在字節碼中佔兩個字節。
get_word 是 y.tab.c 中的 code_word 的相反過程,CodeWord 是個聯合體
優化
typedef union { struct {char c1; char c2;} m; Word w; } CodeWord;
在 code_word 方法
lua
static void code_word (Word n) { CodeWord code; code.w = n; code_byte(code.m.c1); code_byte(code.m.c2); }
能夠看到,設置的時候把值設置給 w, 以後調用 code_byte 分別對 w 的兩個字節生成字節碼。
因爲 CodeWord 是個聯合體,這裏的 w 和 m 是同一片內存,因此 code_word 能夠按預期正確執行。
這樣的聯合體操做是 C 語言裏的一個小技巧,好比測字節序(例如你的處理器體系結構是大端序仍是小端序)的時候就能夠用這樣的技巧。
> PUSHFLOAT
指針
case PUSHFLOAT: { CodeFloat code; get_float(code,pc); tag(top) = T_NUMBER; nvalue(top++) = code.f; } break;
設置棧頂的 Object 類型爲 T_NUMBER,值從當前字節碼處(也就是 pc 指針處的四個字節)取得。值在字節碼中佔四個字節。
get_float 和上面的 get_word 狀況同樣,再也不重複。
> PUSHSTRING
code
case PUSHSTRING: { CodeWord code; get_word(code,pc); tag(top) = T_STRING; svalue(top++) = lua_constant[code.w]; } break;
設置棧頂的 Object 類型爲 T_STRING,值從常量表從取得,下標從當前字節碼處取得。下標占兩個字節。
> PUSHLOCAL0, PUSHLOCAL1, PUSHLOCAL2, PUSHLOCAL3, PUSHLOCAL4,
PUSHLOCAL5, PUSHLOCAL6, PUSHLOCAL7, PUSHLOCAL8, PUSHLOCAL9,
索引
case PUSHLOCAL0: case PUSHLOCAL1: case PUSHLOCAL2: case PUSHLOCAL3: case PUSHLOCAL4: case PUSHLOCAL5: case PUSHLOCAL6: case PUSHLOCAL7: case PUSHLOCAL8: case PUSHLOCAL9: *top++ = *(base + (int)(opcode-PUSHLOCAL0)); break;
設置棧頂的 Object 爲局部變量 N (0<=N<=9),這個也是指令優化。把當前指令 opcode 減去 PUSHLOCAL0 取得偏移量 N。
局部變量是從棧的 base 算起的偏移量,也能夠理解爲數組的下標。
這幾個指令是 PUSHLOCAL 的特化版。
> PUSHLOCAL
內存
case PUSHLOCAL: *top++ = *(base + (*pc++)); break;
設置棧頂的 Object 爲局部變量 N (N 的偏移量從字節碼中取得)。
> PUSHGLOBAL,
case PUSHGLOBAL: { CodeWord code; get_word(code,pc); *top++ = s_object(code.w); } break;
設置棧頂的 Object 爲全局變量 N (N 從全局符號表中取得,它的下標從字節碼中取得,佔兩個字節)。
> PUSHINDEXED,
case PUSHINDEXED: --top; if (tag(top-1) != T_ARRAY) { lua_reportbug ("indexed expression not a table"); return 1; } { Object *h = lua_hashdefine (avalue(top-1), top); if (h == NULL) return 1; *(top-1) = *h; } break;
設置數組元素索引,當前的棧的 top 處爲要設置的元素索引,top-1 爲數組。
經過 lua_hashdefine 把 top 作爲索引設置進數組,返回其所在鍵值對兒 Node 值 val 地址。
設置到 top-1 處,以備後續使用。
> PUSHMARK
case PUSHMARK: tag(top++) = T_MARK; break;
設置棧頂的 Object 爲 T_MARK,這個用於標記,好比在函數調用時會在函數入棧後再入棧一個 T_MARK。
> PUSHOBJECT
case PUSHOBJECT: *top = *(top-3); top++; break;
設置棧頂的 Object 爲棧頂的倒數第 4 個 Object。
> STORELOCAL0, STORELOCAL1, STORELOCAL2, STORELOCAL3, STORELOCAL4,
STORELOCAL5, STORELOCAL6, STORELOCAL7, STORELOCAL8, STORELOCAL9,
case STORELOCAL0: case STORELOCAL1: case STORELOCAL2: case STORELOCAL3: case STORELOCAL4: case STORELOCAL5: case STORELOCAL6: case STORELOCAL7: case STORELOCAL8: case STORELOCAL9: *(base + (int)(opcode-STORELOCAL0)) = *(--top); break;
設置局部變量 N (0<=N<=9)爲棧頂的 Object,這個也是指令優化。把當前指令 opcode 減去 STORELOCAL0 取得偏移量 N。
局部變量是從棧的 base 算起的偏移量,也能夠理解爲數組的下標。
這幾個指令是 STORELOCAL 的特化版。
> STORELOCAL
case STORELOCAL: *(base + (*pc++)) = *(--top); break;
設置局部變量 N (N 的偏移量從字節碼中取得)爲棧頂的 Object。
> STOREGLOBAL
case STOREGLOBAL: { CodeWord code; get_word(code,pc); s_object(code.w) = *(--top); } break;
設置全局變量 N (N 從全局符號表中取得,它的下標從字節碼中取得,佔兩個字節)爲棧頂的 Object。
> STOREINDEXED0
case STOREINDEXED0: if (tag(top-3) != T_ARRAY) { lua_reportbug ("indexed expression not a table"); return 1; } { Object *h = lua_hashdefine (avalue(top-3), top-2); if (h == NULL) return 1; *h = *(top-1); } top -= 3; break;
設置數組元素,數組位於 top-3, 數組索引位於 top-2, 數組值位於 top-1。
設置完成後,這三個 Object 出棧。
> STOREINDEXED
case STOREINDEXED: { int n = *pc++; if (tag(top-3-n) != T_ARRAY) { lua_reportbug ("indexed expression not a table"); return 1; } { Object *h = lua_hashdefine (avalue(top-3-n), top-2-n); if (h == NULL) return 1; *h = *(top-1); } top--; } break;
設置指定偏移量的數組元素,偏移量從字節碼中取得,數組位於 top-3-n,索引位於 top-2-n,值位於棧頂。
設置完成後,棧頂值出棧。
> STORELIST0,
STORELIST
case STORELIST0: case STORELIST: { int m, n; Object *arr; if (opcode == STORELIST0) m = 0; else m = *(pc++) * FIELDS_PER_FLUSH; n = *(pc++); arr = top-n-1; if (tag(arr) != T_ARRAY) { lua_reportbug ("internal error - table expected"); return 1; } while (n) { tag(top) = T_NUMBER; nvalue(top) = n+m; *(lua_hashdefine (avalue(arr), top)) = *(top-1); top--; n--; } } break;
設置數組值,m 爲下標,n 爲數組元素個數。要設置的數組值都位於棧上,棧頂爲最後一個元素。
因此在 while 循環裏給數組賦值是先給下標大的賦值,再給小的賦值,每賦值一個就出棧一個。
> STORERECORD
case STORERECORD: { int n = *(pc++); Object *arr = top-n-1; if (tag(arr) != T_ARRAY) { lua_reportbug ("internal error - table expected"); return 1; } while (n) { CodeWord code; get_word(code,pc); tag(top) = T_STRING; svalue(top) = lua_constant[code.w]; *(lua_hashdefine (avalue(arr), top)) = *(top-1); top--; n--; } } break;
給記錄賦值,n 爲要賦值的個數。被賦值的元素索引從字節碼中取得,右值從棧上取得,賦值後出棧。
記錄是指下標爲常量字符串的表,數組是指下標爲整數的表。具體的區別請參見手冊。
> ADJUST
case ADJUST: { Object *newtop = base + *(pc++); while (top < newtop) tag(top++) = T_NIL; top = newtop; /* top could be bigger than newtop */ } break;
調整棧元素個數,元素個數從字節碼取出。若是新的棧頂高於當前棧頂,當前棧頂到新的棧頂之間的元素賦空。
通常函數調用以後會調用它調整棧。 ADJUST 的下一個字節碼(pc)是須要返回的函數返回值個數。
while 補空的意義就是若是須要返回的函數個數大於實際返回的,則補空。
(反之,須要的返回值少於實際返回的個數的話,多餘的會被丟棄,這是經過設置 top 實現的。)
> CREATEARRAY
case CREATEARRAY: if (tag(top-1) == T_NIL) nvalue(top-1) = 101; else { if (tonumber(top-1)) return 1; if (nvalue(top-1) <= 0) nvalue(top-1) = 101; } avalue(top-1) = lua_createarray(nvalue(top-1)); if (avalue(top-1) == NULL) return 1; tag(top-1) = T_ARRAY; break;
新建數組,元素個數從棧頂取得,若是沒有指定或者指定一個負數,把它修正爲 101。若是棧頂不是個數字,出錯。 新建數組,賦值給棧頂元素,並設置棧頂 Object 類型爲 T_ARRAY。(未完待續)