Lua2.4 打印字節碼 print.c

如今編譯器相關的部分就剩下 luac.c 中的 do_dump 函數的分析了。
這個函數裏面主要有兩種調用,存儲字節碼和打印字節碼。
先來看一下打印字節吧,要打印字節碼,須要在編譯器的命令行選項中有 "-l" 選項。
數組

static void do_dump(TFunc* tf)     /* only for tf==main */
{
 if (dumping) DumpHeader(D);
 while (tf!=NULL)
 {
  TFunc* nf;
  if (listing) PrintFunction(tf);
  if (dumping) DumpFunction(tf,D);
  nf=tf->next;     /* list only built after first main */
  luaI_freefunc(tf);
  tf=nf;
 }
}

打印字節碼調用的就是上面的 PrintFunction,能夠看出,它是經過 listing 條件控制的,而 listing 是在命令行中有 「-l" 選項時纔會爲 1。

實際中用的時候,發現這裏有一個小錯誤。當 -p -l 選項同時使用時(只進行語法分析不 dump 字節碼),PrintFunction 只被調用一次,也就是隻打印主函數的字節碼,其它自定義的函數的字節碼不會被打印。去掉 -p 選項時(也就是同時存儲字節碼到默認輸出 luac.out 文件)則正常。
舉個例子,對於下面的 Lua 腳本:
函數

function add(x, y)
    return x + y
end
print (add(3, 4))

當用 -p -l 執行它時,打印的結果以下:
main of "test.lua" (25 bytes at 00602090)
     0    PUSHFUNCTION    00602120    ; "test.lua":1
     5    STOREGLOBAL    13    ; add
     8    PUSHGLOBAL    7    ; print
    11    PUSHGLOBAL    13    ; add
    14    PUSHBYTE    3
    16    PUSHBYTE    4
    18    CALLFUNC    2 1
    21    CALLFUNC    1 0
    24    RETCODE0
奇怪,函數 add 哪兒去了?

當用 -l 執行它時,打印的結果以下:
main of "test.lua" (25 bytes at 001120D8)
     0    PUSHFUNCTION    00112168    ; "test.lua":1
     5    STOREGLOBAL    13    ; add
     8    PUSHGLOBAL    7    ; print
    11    PUSHGLOBAL    13    ; add
    14    PUSHBYTE    3
    16    PUSHBYTE    4
    18    CALLFUNC    2 1
    21    CALLFUNC    1 0
    24    RETCODE0

function "test.lua":1 (9 bytes at 00112168); used at main+1
     0    ADJUST    2
     2    PUSHLOCAL0    0    ;
     3    PUSHLOCAL1    1    ;
     4    ADDOP
     5    RETCODE    2
     7    RETCODE    2
看到了吧,這裏多了下面的 add 函數的字節碼。
這是爲何?應該是程序出現了錯誤。
分析了一下,發現,當有 -p 選項時,do_dump 代碼裏的 tf->next 爲 NULL,而沒有 -p 選項時是好的。調試了一下,發現,在執行 DumpFunction 以後,tf->next 被賦值,跟到 DumpFunction 裏  ThreadCode 裏發現了下面這句:
ui

 case PUSHFUNCTION:
 {
  CodeCode c;
  p++;
  get_code(c,p);
  c.tf->marked=at;
  c.tf->next=NULL;    /* TODO: remove? */
  lastF=lastF->next=c.tf;
  break;
 }

而在有 -p 選項時,上面這句不執行,因此 tf->next 沒有賦值,上面的打印字節碼就打印不了函數 add 的字節碼了。

正常狀況下,主函數和全部的 Lua 腳本中定義的函數編譯後造成一個 TFunc 鏈,函數的字節碼存在 TFunc 的 code 字段中。

print.h 裏定義了了一個字節碼指令的名字數組,它在打印字節碼的時候會用到。
看下 PrintFunction 的代碼
lua

void PrintFunction(TFunc* tf)
{
 if (IsMain(tf))
  printf("\nmain of \"%s\" (%d bytes at %p)\n",tf->fileName,tf->size,tf);
 else
  printf("\nfunction \"%s\":%d (%d bytes at %p); used at main+%d\n",
 tf->fileName,tf->lineDefined,tf->size,tf,tf->marked);
 V=tf->locvars;
 PrintCode(tf->code,tf->code+tf->size);
}

主函數和用戶自定義的函數的打印出來的描述信息不同,經過 IsMain 宏判斷是不是主函數。
#define IsMain(f)    (f->lineDefined==0)
用戶自定義函數的定義行必定爲大於 0 的值,而主函數設定爲 0。

而後,調用 PrintCode 打印字節碼指令。
命令行

static void PrintCode(Byte* code, Byte* end)
{
 Byte* p;
 for (p=code; p!=end;)
 {
 OpCode op=(OpCode)*p;
 if (op>SETLINE) op=SETLINE+1;
 printf("%6d\t%s",p-code,OpCodeName[op]);
 switch (op)
 {
/*cases and other codes*/
}
}
}

打印指令的位置和指令的名字,就是 printf 那一句。
若是是非法指令的話,就是 op>SETLINE,會打印一個空字符串。由於 OpCodeName 數組中 "SETLINE" 下一個字符串就是空串 「」。
接着是一個 switch case 打印其它指令中的一些其它數據相關的信息。包括指令的數據部分和會用到的字符串相關的部分。對比打印出來的字節碼很容易看出代碼的用意,這部分至關於對字節碼進行了一個簡單的解析(說它簡單是和虛擬機中真正執行字節碼時的對比)。因此,就不對它進行一行行的分析了。

----------------------------------------
到目前爲止的問題:
> do_dump 方法裏調的 dump 相關的方法是幹什麼的?
----------------------------------------
調試

相關文章
相關標籤/搜索