成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳解關于Lua源碼分析學習教程

移動開發 iOS
關于Lua源碼分析學習教程是本文要介紹的內容,主要來了解LUA中源碼的使用方法。Lua首先將源程序編譯成為字節碼,然后交由虛擬機解釋執行.對于每一個函數,Lua的編譯器將創建一個原型.

關于Lua源碼分析學習教程是本文要介紹的內容,主要來了解LUA源碼的使用方法。Lua首先將源程序編譯成為字節碼,然后交由虛擬機解釋執行。對于每一個函數,Lua的編譯器將創建一個原型(prototype),它由一組指令及其使用到的常量組成[1]。最初的Lua虛擬機是基于棧的。到1993年,Lua5.0版本,采用了基于寄存器的虛擬機,使得Lua的解釋效率得到提升。

體系結構與指令系統

與虛擬機和指令相關的文件主要有兩個: lopcodes.c 和 lvm.c。從名稱可以看出來,這兩個文件分別用于描述操作碼(指令)和虛擬機。

首先來看指令:

Lua共有38條指令,在下面兩處地方分別描述了這些指令的名稱和模式, 如下:

  1. lopcodes.c:16  
  2. const char *const luaP_opnames[NUM_OPCODES+1] = {  
  3. "MOVE",  
  4. "LOADK",  
  5. "LOADBOOL",  
  6. "LOADNIL",  
  7. "GETUPVAL",  
  8. "GETGLOBAL",  
  9. "GETTABLE",  
  10. "SETGLOBAL",  
  11. "SETUPVAL",  
  12. "SETTABLE",  
  13. "NEWTABLE",  
  14. "SELF",  
  15. "ADD",  
  16. "SUB",  
  17. "MUL",  
  18. "DIV",  
  19. "MOD",  
  20. "POW",  
  21. "UNM",  
  22. "NOT",  
  23. "LEN",  
  24. "CONCAT",  
  25. "JMP",  
  26. "EQ",  
  27. "LT",  
  28. "LE",  
  29. "TEST",  
  30. "TESTSET",  
  31. "CALL",  
  32. "TAILCALL",  
  33. "RETURN",  
  34. "FORLOOP",  
  35. "FORPREP",  
  36. "TFORLOOP",  
  37. "SETLIST",  
  38. "CLOSE",  
  39. "CLOSURE",  
  40. "VARARG",  
  41. NULL  
  42. };  
  43.  
  44. #define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m))  
  45.  
  46. const lu_byte luaP_opmodes[NUM_OPCODES] = {  
  47. /*       T A    B       C     mode           opcode    */  
  48. opmode(0, 1, OpArgR, OpArgN, iABC)         /* OP_MOVE */  
  49. ,opmode(0, 1, OpArgK, OpArgN, iABx)        /* OP_LOADK */  
  50. ,opmode(0, 1, OpArgU, OpArgU, iABC)        /* OP_LOADBOOL */  
  51. ,opmode(0, 1, OpArgR, OpArgN, iABC)        /* OP_LOADNIL */  
  52. ,opmode(0, 1, OpArgU, OpArgN, iABC)        /* OP_GETUPVAL */  
  53. ,opmode(0, 1, OpArgK, OpArgN, iABx)        /* OP_GETGLOBAL */  
  54. ,opmode(0, 1, OpArgR, OpArgK, iABC)        /* OP_GETTABLE */  
  55. ,opmode(0, 0, OpArgK, OpArgN, iABx)        /* OP_SETGLOBAL */  
  56. ,opmode(0, 0, OpArgU, OpArgN, iABC)        /* OP_SETUPVAL */  
  57. ,opmode(0, 0, OpArgK, OpArgK, iABC)        /* OP_SETTABLE */  
  58. ,opmode(0, 1, OpArgU, OpArgU, iABC)        /* OP_NEWTABLE */  
  59. ,opmode(0, 1, OpArgR, OpArgK, iABC)        /* OP_SELF */  
  60. ,opmode(0, 1, OpArgK, OpArgK, iABC)        /* OP_ADD */  
  61. ,opmode(0, 1, OpArgK, OpArgK, iABC)        /* OP_SUB */  
  62. ,opmode(0, 1, OpArgK, OpArgK, iABC)        /* OP_MUL */  
  63. ,opmode(0, 1, OpArgK, OpArgK, iABC)        /* OP_DIV */  
  64. ,opmode(0, 1, OpArgK, OpArgK, iABC)        /* OP_MOD */  
  65. ,opmode(0, 1, OpArgK, OpArgK, iABC)        /* OP_POW */  
  66. ,opmode(0, 1, OpArgR, OpArgN, iABC)        /* OP_UNM */  
  67. ,opmode(0, 1, OpArgR, OpArgN, iABC)        /* OP_NOT */  
  68. ,opmode(0, 1, OpArgR, OpArgN, iABC)        /* OP_LEN */  
  69. ,opmode(0, 1, OpArgR, OpArgR, iABC)        /* OP_CONCAT */  
  70. ,opmode(0, 0, OpArgR, OpArgN, iAsBx)        /* OP_JMP */  
  71. ,opmode(1, 0, OpArgK, OpArgK, iABC)        /* OP_EQ */  
  72. ,opmode(1, 0, OpArgK, OpArgK, iABC)        /* OP_LT */  
  73. ,opmode(1, 0, OpArgK, OpArgK, iABC)        /* OP_LE */  
  74. ,opmode(1, 1, OpArgR, OpArgU, iABC)        /* OP_TEST */  
  75. ,opmode(1, 1, OpArgR, OpArgU, iABC)        /* OP_TESTSET */  
  76. ,opmode(0, 1, OpArgU, OpArgU, iABC)        /* OP_CALL */  
  77. ,opmode(0, 1, OpArgU, OpArgU, iABC)        /* OP_TAILCALL */  
  78. ,opmode(0, 0, OpArgU, OpArgN, iABC)        /* OP_RETURN */  
  79. ,opmode(0, 1, OpArgR, OpArgN, iAsBx)        /* OP_FORLOOP */  
  80. ,opmode(0, 1, OpArgR, OpArgN, iAsBx)        /* OP_FORPREP */  
  81. ,opmode(1, 0, OpArgN, OpArgU, iABC)        /* OP_TFORLOOP */  
  82. ,opmode(0, 0, OpArgU, OpArgU, iABC)        /* OP_SETLIST */  
  83. ,opmode(0, 0, OpArgN, OpArgN, iABC)        /* OP_CLOSE */  
  84. ,opmode(0, 1, OpArgU, OpArgN, iABx)        /* OP_CLOSURE */  
  85. ,opmode(0, 1, OpArgU, OpArgN, iABC)        /* OP_VARARG */  
  86. }; 

前面一個數組容易理解,表示了每條指令的名稱。后面一個數組表示的是指令的模式。奇怪的符號讓人有些費解。在看模式之前, 首先來看Lua指令的格式,如圖:

詳解關于Lua源碼分析學習教程

如上圖, Lua的指令可以分成三種形式. 即在上面的模式數組中也可以看到的iABC, iABx 和 iAsBx. 對于三種形式的指令來說, 前兩部分都是一樣的, 分別是6位的操作碼和8位A操作數; 區別在于, 后面部是分割成為兩個長度為9位的操作符(B, C),一個無符號的18位操作符Bx還是有符號的18位操作符sBx. 這些定義的代碼如下:

  1. lopcodes.c : 34  
  2. /*  
  3. ** size and position of opcode arguments.  
  4. */  
  5. #define SIZE_C        9  
  6. #define SIZE_B        9  
  7. #define SIZE_Bx        (SIZE_C + SIZE_B)  
  8. #define SIZE_A        8  
  9.  
  10. #define SIZE_OP        6  
  11.  
  12. #define POS_OP        0  
  13. #define POS_A        (POS_OP + SIZE_OP)  
  14. #define POS_C        (POS_A + SIZE_A)  
  15. #define POS_B        (POS_C + SIZE_C)  
  16. #define POS_Bx        POS_C 

再來看指令的操作模式, Lua使用一個字節來表示指令的操作模式. 具體的含義如下:

1、使用最高位來表示是否是一條測試指令. 之所以將這一類型的指令特別地標識出來, 是因為Lua的指令長度是32位,對于分支指令來說, 要想在這32位中既表示兩個操作數來做比較, 同時還要表示一個跳轉的地址, 是很困難的. 因此將這種指令分成兩條, 第一條是測試指令, 緊接著一條無條件跳轉. 如果判斷條件成立則將PC(Program Counter, 指示下一條要執行的指令)加一, 跳過下一條無條件跳轉指令, 繼續執行; 否則跳轉.

2、第二位用于表示A操作數是否被設置

3、接下來的二位用于表示操作數B的格式,OpArgN表示操作數未被使用, OpArgU表示操作數被使用(立即數?), OpArgR表示表示操作數是寄存器或者跳轉的偏移量, OpArgK表示操作數是寄存器或者常量.

最后, 給出Lua虛擬機的體系結構圖(根據源代碼分析得出):

詳解關于Lua源碼分析學習教程

首先, 我們注意到, Lua的解釋器還是一個以棧為中心的結構. 在lua_State這個結構中,有許多個字段用于描述這個結構.stack用于指向絕對棧底, 而base指向了當前正在執行的函數的第一個參數, 而top指向棧頂的第一個空元素.

我們可以看到,這個體系結構中并沒有獨立出來的寄存器. 從以下代碼來看:

  1. lvm.c:343  
  2. #define RA(i)    (base+GETARG_A(i))  
  3. /* to be used after possible stack reallocation */  
  4. #define RB(i)    check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))  
  5. #define RC(i)    check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))  
  6. #define RKB(i)    check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \  
  7.     ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))  
  8. #define RKC(i)    check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \  
  9.     ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))  
  10. #define KBx(i)    check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) 

當指令操作數的類型是寄存器時,它的內容是以base為基址在棧上的索引值.如圖所示.寄存器實際是base之上棧元素的別名;當指令操作數的類型的常數時, 它首先判斷B操作數的最位是否為零.如果是零,則按照和寄存器的處理方法一樣做,如果不是零,則在常數表中找相應的值.

我們知道Lua中函數的執行過程是這樣的. 首先將函數壓棧,然后依次將參數壓棧,形成圖中所示的棧的內容. 因此R[0]到R[n]也分別表示了Arg[1]到Arg[N+1].在第一個參數之下,就是當前正在執行的函數,對于Lua的函數(相對C函數)來說,它是指向類型為 Prototype的TValue, 在Prototype中字段code指向了一個數組用來表示組成這個函數的所有指令,字段k指向一個數組來表示這個函數使用到的所有常量.最后,Lua在解釋執行過程中有專門的變量pc來指向下一條要執行的指令.

指令解釋器

有了前面對指令格式和體系結構的介紹,現在我們可以進入正題, 來看看Lua的指令是如何執行的了.主函數如下:

  1. lvm.c:373  
  2. void luaV_execute (lua_State *L, int nexeccalls) {  
  3. LClosure *cl;  
  4. StkId base;  
  5. TValue *k;  
  6. const Instruction *pc;  
  7. reentry: /* entry point */  
  8. lua_assert(isLua(L->ci));  
  9. pc = L->savedpc;  
  10. cl = &clvalue(L->ci->func)->l;  
  11. base = L->base;  
  12. k = cl->p->k; 

這是最開始的初始化過程.其中, pc被初始化成為了L->savedpc,base被初始化成為了L->base, 即程序從L->savedpc開始執行 (在下一篇專題中,將會介紹到 L->savedpc在函數調用的預處理過程中指向了當前函數的code),而L->base指向棧中當前函數的下一個位置.cl表示當前正在執行閉包(當前可以理解成為函數),k指向當前閉包的常量表.

接下來(注意,為了專注主要邏輯, 我將其中用于Debugger支持,斷言等代碼省略了):

  1. /* main loop of interpreter */  
  2. for (;;) {  
  3.     const Instruction i = *pc++;  
  4.     StkId ra;  
  5.     /* 省略Debugger支持和Coroutine支持*/  
  6.     /* warning!! several calls may realloc the stack and invalidate `ra' */  
  7.     ra = RA(i);  
  8.     /* 省略斷言 */  
  9.     switch (GET_OPCODE(i)) { 

進入到解釋器的主循環,處理很簡單,取得當前指令,pc遞增,初始化ra,然后根據指令的操作碼進行選擇. 接下來的代碼是什么樣的, 估計大家都能想到,一大串的case來指示每條指令的執行.具體的實現可以參考源碼, 在這里不對每一條指令展開, 只是對其中有主要的幾類指令進行說明:

傳值類的指令,與MOVE為代表:

  1. lvm.c:403  
  2.       case OP_MOVE: {  
  3.         setobjs2s(L, ra, RB(i));  
  4.         continue;  
  5.       }  
  6. lopcodes:154  
  7. OP_MOVE,/*    A B    R(A) :R(B)                    */  
  8. lobject.h:161  
  9. #define setobj(L,obj1,obj2) \  
  10. { const TValue *o2=(obj2); TValue *o1=(obj1); \  
  11.     o1->value = o2->value; o1->tt=o2->tt; \  
  12.     checkliveness(G(L),o1); }  
  13.  
  14. /*  
  15. ** different types of sets, according to destination  
  16. */  
  17. /* from stack to (same) stack */  
  18. #define setobjs2s    setobj 

從注釋來看, 這條指令是將操作數A,B都做為寄存器,然后將B的值給A. 而實現也是簡單明了,只使用了一句. 宏展開以后, 可以看到, R[A],R[B]的類型是TValue, 只是將這兩域的值傳過來即可. 對于可回收對象來說,真實值不會保存在棧上,所以只是改了指針,而對于非可回收對象來說,則是直接將值從R[B]賦到R[A].

數值運算類指令,與ADD為代表:

  1. lvm.c:470  
  2.       case OP_ADD: {  
  3.         arith_op(luai_numadd, TM_ADD);  
  4.         continue;  
  5.       }  
  6. lvm.c:360  
  7. #define arith_op(op,tm) { \  
  8.         TValue *rb = RKB(i); \  
  9.         TValue *rc = RKC(i); \  
  10.         if (ttisnumber(rb) && ttisnumber(rc)) { \  
  11.           lua_Number nb = nvalue(rb), nc = nvalue(rc); \  
  12.           setnvalue(ra, op(nb, nc)); \  
  13.         } \  
  14.         else \  
  15.           Protect(Arith(L, ra, rb, rc, tm)); \  
  16.       }  
  17. lopcodes.c:171  
  18. OP_ADD,/*    A B C    R(A) :RK(B) + RK(C)                */ 

如果兩個操作數都是數值的話,關鍵的一行是:

  1. setnvalue(ra,op(nb,nc)); 

即兩個操作數相加以后,把值賦給R[A].值得注意的是,操作數B,C都是RK, 即可能是寄存器也可能是常量,這最決于最B和C的最高位是否為1,如果是1,則是常量,反之則是寄存器.具體可以參考宏ISK的實現.

如果兩個操作數不是數值,即調用了Arith函數,它嘗試將兩個操作轉換成數值進行計算,如果無法轉換,則使用元表機制.該函數的實現如下:

  1. lvm.c:313  
  2. static void Arith (lua_State *L, StkId ra, const TValue *rb,  
  3.                    const TValue *rc, TMS op) {  
  4. TValue tempb, tempc;  
  5. const TValue *b, *c;  
  6. if ((b = luaV_tonumber(rb, &tempb)) != NULL &&  
  7.       (c = luaV_tonumber(rc, &tempc)) != NULL) {  
  8.     lua_Number nb = nvalue(b), nc = nvalue(c);  
  9.     switch (op) {  
  10.       case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break;  
  11.       case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break;  
  12.       case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break;  
  13.       case TM_DIV: setnvalue(ra, luai_numdiv(nb, nc)); break;  
  14.       case TM_MOD: setnvalue(ra, luai_nummod(nb, nc)); break;  
  15.       case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break;  
  16.       case TM_UNM: setnvalue(ra, luai_numunm(nb)); break;  
  17.       default: lua_assert(0); break;  
  18.     }  
  19. }  
  20. else if (!call_binTM(L, rb, rc, ra, op))  
  21.     luaG_aritherror(L, rb, rc);  

在上面call_binTM用于調用到元表中的元方法,因為在Lua以前的版本中元方法也被叫做tag method, 所以函數最后是以TM結尾的.

  1. lvm:163  
  2. static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,  
  3.                        StkId res, TMS event) {  
  4. const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */  
  5. if (ttisnil(tm))  
  6.     tm = luaT_gettmbyobj(L, p2, event); /* try second operand */  
  7. if (!ttisfunction(tm)) return 0;  
  8. callTMres(L, res, tm, p1, p2);  
  9. return 1;  

在 這個函數中,試著從二個操作數中找到其中一個操作數的元方法(第一個操作數優先), 這里event表示具體哪一個元方法,找到了之后,再使用函數callTMres()去調用相應的元方法. callTMres()的實現很簡單,只是將元方法,第一,第二操作數先后壓棧,再調用并取因返回值.具體如下:

  1. lvm.c:82  
  2. static void callTMres (lua_State *L, StkId res, const TValue *f,  
  3.                         const TValue *p1, const TValue *p2) {  
  4. ptrdiff_t result = savestack(L, res);  
  5. setobj2s(L, L->top, f); /* push function */  
  6. setobj2s(L, L->top+1, p1); /* 1st argument */  
  7. setobj2s(L, L->top+2, p2); /* 2nd argument */  
  8. luaD_checkstack(L, 3);  
  9. L->top += 3;  
  10. luaD_call(L, L->top - 3, 1);  
  11. res = restorestack(L, result);  
  12. L->top--;  
  13. setobjs2s(L, res, L->top);  

邏輯運算類指令,與EQ為代表:

  1. lvm.c:541  
  2.       case OP_EQ: {  
  3.         TValue *rb = RKB(i);  
  4.         TValue *rc = RKC(i);  
  5.         Protect(  
  6.           if (equalobj(L, rb, rc) == GETARG_A(i))  
  7.             dojump(L, pc, GETARG_sBx(*pc));  
  8.         )  
  9.         pc++;  
  10.         continue;  
  11.       }  
  12. lopcodes.c:185  
  13. OP_EQ,/*    A B C    if ((RK(B) == RK(C)) ~= A) then pc++        */ 

在這條指令實現的過程中,equalobj與之前的算術運算類似,讀者可以自行分析.關鍵看它是如果實現中跳轉的,如果RK[B]==RK[C]并且A為1 的情況下(即條件為真),則會使用pc取出下一條指令,調用dojump進行跳轉,否則pc++,掛空緊接著的無條件跳轉指令. dojump的實現如下:

  1. lvm.c:354  
  2. #define dojump(L,pc,i)    {(pc) += (i); luai_threadyield(L);} 

luai_threadyield只是順序地調用lua_unlock和lua_lock,這里為釋放一次鎖,使得別的線程可以得到調度.

函數調用類指令,與CALL為代表:

  1. lvm.c:582  
  2.       case OP_CALL: {  
  3.         int b = GETARG_B(i);  
  4.         int nresults = GETARG_C(i) - 1;  
  5.         if (b != 0) L->top = ra+b; /* else previous instruction set top */  
  6.         L->savedpc = pc;  
  7.         switch (luaD_precall(L, ra, nresults)) {  
  8.           case PCRLUA: {  
  9.             nexeccalls++;  
  10.             goto reentry; /* restart luaV_execute over new Lua function */  
  11.           }  
  12.           case PCRC: {  
  13.             /* it was a C function (`precall' called it); adjust results */  
  14.             if (nresults >= 0) L->top = L->ci->top;  
  15.             base = L->base;  
  16.             continue;  
  17.           }  
  18.           default: {  
  19.             return; /* yield */  
  20.           }  
  21.         }  
  22.       }  
  23. lopcodes.c:192  
  24.  
  25. OP_CALL,/*    A B C    R(A), ... ,R(A+C-2) :R(A)(R(A+1), ... ,R(A+B-1)) */ 

這一條指令將在下一個介紹Lua函數調用規范的專題中詳細介紹. 在這里只是簡單地說明CALL指令的R[A]表示的是即將要調用的函數,而B和C則分別表示參數個數加1,和返回值個數加1. 之所以這里需要加1,其原因是:B和C使用零來表示變長的參數和變長的返回值,而實際參數個數就向后推了一個.

指令的介紹就先到此為止了, 其它的指令的實現也比較類似.仔細閱讀源碼就可很容易地分析出它的意義來. 下一篇將是一個專題, 詳細地介紹Lua中函數的調用是如何實現的.

小結:詳解關于Lua源碼分析學習教程的內容介紹完了,希望通過本文的學習能對你有所幫助!

責任編輯:zhaolei 來源: 互聯網
相關推薦

2011-08-24 15:42:38

LUA源代碼

2011-08-23 17:06:03

2011-08-25 16:20:33

Lua腳本變量

2011-08-24 14:14:13

LUA環境 配置

2011-08-23 15:34:56

Lua模式 匹配

2011-08-24 11:03:33

LUA環境 安裝

2011-08-23 16:37:05

Lua數學庫

2011-08-24 15:34:44

MinGWLua環境配置

2011-09-02 13:51:00

PhoneGap框架HTML5

2011-08-24 11:08:09

Lua

2011-08-24 17:09:35

LUA閉包函數

2011-08-25 10:07:24

Lua 5.0函數編譯器

2011-08-31 10:20:26

MTK驅動開發

2011-08-24 13:27:07

Lua 游戲C接口腳本

2011-08-24 16:59:59

LuaModule

2011-08-23 13:54:10

LUA全局變量

2011-08-23 16:48:41

Lua 5.1API 函數

2011-08-24 14:33:14

LUA開發環境Decoda

2011-08-25 17:25:55

LUADelphi

2011-08-25 14:03:32

UbuntuLUA安裝
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91大神在线资源观看无广告 | 亚洲一区精品在线 | 亚洲一区精品在线 | 一级少妇女片 | 久久剧场| 国产精品久久久久久久久免费桃花 | 国产精品3区| 久久不射电影网 | 国产香蕉视频在线播放 | 日韩欧美大片在线观看 | 久久婷婷国产 | 亚洲欧美激情精品一区二区 | 国产免费播放视频 | 日韩精品一区二区久久 | 草草在线观看 | 黄网站在线观看 | 99久久中文字幕三级久久日本 | 成人免费网站视频 | 国产激情精品视频 | 亚洲精品在线免费观看视频 | 欧美日韩电影一区 | 成人亚洲综合 | 狠狠的干 | 精久久久久 | 亚洲男人天堂av | 久久久久久黄 | 人人草人人干 | 成人网视频 | 精品三区 | 超碰8| 国产成人精品一区二区三 | 国产精品久久久久久久久久久免费看 | 女同av亚洲女人天堂 | 九九综合| 亚洲免费视频网站 | 国产精品美女久久久免费 | 国产高清精品一区二区三区 | 亚洲精品久久久一区二区三区 | 欧美精品在线观看 | 高清一区二区视频 | 国产一区二区在线播放 |