自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

詳解關(guān)于Lua源碼分析學(xué)習(xí)教程

移動(dòng)開發(fā) iOS
關(guān)于Lua源碼分析學(xué)習(xí)教程是本文要介紹的內(nèi)容,主要來了解LUA中源碼的使用方法。Lua首先將源程序編譯成為字節(jié)碼,然后交由虛擬機(jī)解釋執(zhí)行.對(duì)于每一個(gè)函數(shù),Lua的編譯器將創(chuàng)建一個(gè)原型.

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

體系結(jié)構(gòu)與指令系統(tǒng)

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

首先來看指令:

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. }; 

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

詳解關(guān)于Lua源碼分析學(xué)習(xí)教程

如上圖, Lua的指令可以分成三種形式. 即在上面的模式數(shù)組中也可以看到的iABC, iABx 和 iAsBx. 對(duì)于三種形式的指令來說, 前兩部分都是一樣的, 分別是6位的操作碼和8位A操作數(shù); 區(qū)別在于, 后面部是分割成為兩個(gè)長(zhǎng)度為9位的操作符(B, C),一個(gè)無符號(hào)的18位操作符Bx還是有符號(hào)的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使用一個(gè)字節(jié)來表示指令的操作模式. 具體的含義如下:

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

2、第二位用于表示A操作數(shù)是否被設(shè)置

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

最后, 給出Lua虛擬機(jī)的體系結(jié)構(gòu)圖(根據(jù)源代碼分析得出):

詳解關(guān)于Lua源碼分析學(xué)習(xí)教程

首先, 我們注意到, Lua的解釋器還是一個(gè)以棧為中心的結(jié)構(gòu). 在lua_State這個(gè)結(jié)構(gòu)中,有許多個(gè)字段用于描述這個(gè)結(jié)構(gòu).stack用于指向絕對(duì)棧底, 而base指向了當(dāng)前正在執(zhí)行的函數(shù)的第一個(gè)參數(shù), 而top指向棧頂?shù)牡谝粋€(gè)空元素.

我們可以看到,這個(gè)體系結(jié)構(gòu)中并沒有獨(dú)立出來的寄存器. 從以下代碼來看:

  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)) 

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

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

指令解釋器

有了前面對(duì)指令格式和體系結(jié)構(gòu)的介紹,現(xiàn)在我們可以進(jìn)入正題, 來看看Lua的指令是如何執(zhí)行的了.主函數(shù)如下:

  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開始執(zhí)行 (在下一篇專題中,將會(huì)介紹到 L->savedpc在函數(shù)調(diào)用的預(yù)處理過程中指向了當(dāng)前函數(shù)的code),而L->base指向棧中當(dāng)前函數(shù)的下一個(gè)位置.cl表示當(dāng)前正在執(zhí)行閉包(當(dāng)前可以理解成為函數(shù)),k指向當(dāng)前閉包的常量表.

接下來(注意,為了專注主要邏輯, 我將其中用于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)) { 

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

傳值類的指令,與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 

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

數(shù)值運(yùn)算類指令,與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)                */ 

如果兩個(gè)操作數(shù)都是數(shù)值的話,關(guān)鍵的一行是:

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

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

如果兩個(gè)操作數(shù)不是數(shù)值,即調(diào)用了Arith函數(shù),它嘗試將兩個(gè)操作轉(zhuǎn)換成數(shù)值進(jìn)行計(jì)算,如果無法轉(zhuǎn)換,則使用元表機(jī)制.該函數(shù)的實(shí)現(xiàn)如下:

  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用于調(diào)用到元表中的元方法,因?yàn)樵贚ua以前的版本中元方法也被叫做tag method, 所以函數(shù)最后是以TM結(jié)尾的.

  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;  

在 這個(gè)函數(shù)中,試著從二個(gè)操作數(shù)中找到其中一個(gè)操作數(shù)的元方法(第一個(gè)操作數(shù)優(yōu)先), 這里event表示具體哪一個(gè)元方法,找到了之后,再使用函數(shù)callTMres()去調(diào)用相應(yīng)的元方法. callTMres()的實(shí)現(xiàn)很簡(jiǎn)單,只是將元方法,第一,第二操作數(shù)先后壓棧,再調(diào)用并取因返回值.具體如下:

  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);  

邏輯運(yùn)算類指令,與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++        */ 

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

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

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

函數(shù)調(diào)用類指令,與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)) */ 

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

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

小結(jié):詳解關(guān)于Lua源碼分析學(xué)習(xí)教程的內(nèi)容介紹完了,希望通過本文的學(xué)習(xí)能對(duì)你有所幫助!

責(zé)任編輯:zhaolei 來源: 互聯(lián)網(wǎng)
相關(guān)推薦

2011-08-24 15:42:38

LUA源代碼

2011-08-25 16:20:33

Lua腳本變量

2011-08-23 17:06:03

2011-08-23 15:34:56

Lua模式 匹配

2011-08-24 14:14:13

LUA環(huán)境 配置

2011-08-24 11:03:33

LUA環(huán)境 安裝

2011-08-23 16:37:05

Lua數(shù)學(xué)庫

2011-08-24 15:34:44

MinGWLua環(huán)境配置

2011-09-02 13:51:00

PhoneGap框架HTML5

2011-08-24 17:09:35

LUA閉包函數(shù)

2011-08-24 11:08:09

Lua

2011-08-25 10:07:24

Lua 5.0函數(shù)編譯器

2011-08-24 13:27:07

Lua 游戲C接口腳本

2011-08-31 10:20:26

MTK驅(qū)動(dòng)開發(fā)

2011-08-24 16:59:59

LuaModule

2011-08-23 16:48:41

Lua 5.1API 函數(shù)

2011-08-23 13:54:10

LUA全局變量

2011-08-25 14:03:32

UbuntuLUA安裝

2011-08-24 14:33:14

LUA開發(fā)環(huán)境Decoda

2011-08-25 17:25:55

LUADelphi
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)