Lua2.4 場景恢復 undump.c

這一節看看 luaI_undump1 是如何場景恢復的。

函數

/*
** load one chunk from a file.
** return list of functions found, headed by main, or NULL at EOF.
*/
TFunc* luaI_undump1(FILE* D)
{
 while (1)
 {
  int c=getc(D);
  if (c==ID_CHUNK)
  {
   LoadChunk(D);
   return Main;
  }
  else if (c==EOF)
   return NULL;
  else
   lua_error("not a lua binary file");
 }
}

程序一開始,先檢查文件的第一個字符是不是 ID_CHUNK,若是是,Load 一個塊,返回主函數 Main。
這個 Main 在 LoadChunk(D) 裏被賦值了。
若是第一個字符不是 ID_CHUNK ,說明不是目標二進制文件,返回空。
其它狀況,出錯。

接着看看 LoadChunk。
lua

static void LoadChunk(FILE* D)
{
 LoadHeader(D);
 while (1)
 {
  int c=getc(D);
  if (c==ID_FUN) LoadFunction(D); else { ungetc(c,D); break; }
 }
}

回憶一下 dump 時的操做,這裏恰好是 dump 的逆過程。對比 dump 時的寫,這裏的讀就比較容易閱讀。
先讀全局的一些信息,和 DumpHeader 相對應。
先回憶一下 DumpHeader,代碼以下:
code

void DumpHeader(FILE* D)
{
 Word w=TEST_WORD;
 float f=TEST_FLOAT;
 fputc(ID_CHUNK,D);
 fputs(SIGNATURE,D);
 fputc(VERSION,D);
 fwrite(&w,sizeof(w),1,D);
 fwrite(&f,sizeof(f),1,D);
}

再看 LoadHeader
ip

static void LoadHeader(FILE* D)     /* TODO: error handling */
{
 Word w,tw=TEST_WORD;
 float f,tf=TEST_FLOAT;
 LoadSignature(D);
 getc(D);     /* skip version */
 fread(&w,sizeof(w),1,D);     /* test word */
 if (w!=tw)
 {
  swapword=1;
  warn("different byte order");
 }
 fread(&f,sizeof(f),1,D);     /* test float */
 if (f!=tf)
 {
  Byte* p=(Byte*)&f;     /* TODO: need union? */
  Byte t;
  swapfloat=1;
  t=p[0]; p[0]=p[3]; p[3]=t;
  t=p[1]; p[1]=p[2]; p[2]=t;
  if (f!=tf)     /* TODO: try another perm? */
   lua_error("different float representation");
  else
   warn("different byte order in floats");
 }
}

是否是看起來差很少。
上來先讀一個 SIGNATURE,若是不是的話,出錯。undump 時的每一位是必須嚴格相等的,若是任何一處和預期的不一樣,就直接出錯了。
getc(D),跳過版本信息,註釋的比較清楚。
接着下面開始讀 TEST_WORD 和 TEST_FLOAT 以肯定字節序。若是讀到的和這裏的宏定義的不同,表示須要交換字節序。swapword 和 swapfloat 置位。

回到 LoadChunk,LoadHeader 以後,開始 LoadFunction,一如 dump 時的順序。
函數在 dump 的時候是在標記 ID_FUN 以後開始的,因此這裏的 LoadFunction 先判斷是不是 ID_FUN,若是不是的話,表示表示是別的內容,放回字符並返回。
內存

static void LoadFunction(FILE* D)
{
 TFunc* tf=new(TFunc);
 tf->next=NULL;
 tf->locvars=NULL;
 tf->size=LoadSize(D);
 tf->lineDefined=LoadWord(D);
 if (IsMain(tf))     /* new main */
 {
  tf->fileName=LoadNewString(D);
  Main=lastF=tf;
 }
 else     /* fix PUSHFUNCTION */
 {
  CodeCode c;
  Byte* p;
  tf->marked=LoadWord(D);
  tf->fileName=Main->fileName;
  p=Main->code+tf->marked;
  c.tf=tf;
  *p++=c.m.c1; *p++=c.m.c2; *p++=c.m.c3; *p++=c.m.c4;
  lastF=lastF->next=tf;
 }
 tf->code=LoadBlock(tf->size,D);
 if (swapword || swapfloat) FixCode(tf->code,tf->code+tf->size);
 while (1)     /* unthread */
 {
  int c=getc(D);
  if (c==ID_VAR)     /* global var */
  {
   int i=LoadWord(D);
   char* s=LoadString(D);
   int v=luaI_findsymbolbyname(s);
   Unthread(tf->code,i,v);
  }
  else if (c==ID_STR)     /* constant string */
  {
   int i=LoadWord(D);
   char* s=LoadString(D);
   int v=luaI_findconstantbyname(s);
   Unthread(tf->code,i,v);
  }
  else
  {
   ungetc(c,D);
   break;
  }
 }
}

函數一開始新建一個 TFunc。
接着讀取它的 size 和 lineDefined。
判斷函數是否爲主函數,若是是主函數,則設置文件名,和 Main 字段。lastF 字段是指最後一次讀取到的函數。
若是函數非主函數,讀取它的 marked 字段。這個字段在 dump 那一節忘記說了,非主函數的 marked 是指它在主函數裏的字節碼偏移量。
看這裏是如何用的,把當前函數的地址賦到主函數相應的字節碼處。
字符串

  p=Main->code+tf->marked;
  c.tf=tf;
  *p++=c.m.c1; *p++=c.m.c2; *p++=c.m.c3; *p++=c.m.c4;

通過這個賦值,原來 dump 的主函數裏 PUSHFUNCTION 後面的 marked 值就變成了真正的這個函數的內存地址了。
而後,把非主函數連接到函數鏈表中 lastF=lastF->next=tf;
接着 LoadBlock 讀取字節碼。
若是須要交換字節序,剛用 FixCode 交換字節序。
接下來的 while(1)  循環就是把 dump 的符號和字符串設置回運行環境中。
符號以 ID_VAR 打頭,每設置一個符號到運行環境中,都會把相應的字節碼中引用到它的地方做相應的 Unthread,這個是 dump 時的 ThreadCode 的逆過程。對比 ThreadCode 比較容易理解這裏的 Unthread,或者看下前面的那個 dump 裏的例子,也是比較清楚 ThreadCode 作了哪些事兒,而這裏是把它還原回去了。
get

static void Unthread(Byte* code, int i, int v)
{
 while (i!=0)
 {
  CodeWord c;
  Byte* p=code+i;
  get_word(c,p);
  i=c.w;
  c.w=v;
  p[-2]=c.m.c1;
  p[-1]=c.m.c2;
 }
}

到此,程序已經準備好運行了,開始虛擬機跑字節碼了,也就是編譯以後的內容了。
虛擬機

相關文章
相關標籤/搜索