这一节看看 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");
}
}
/*
** 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。
static void LoadChunk(FILE* D)
{
LoadHeader(D);
while (1)
{
int c=getc(D);
if (c==ID_FUN) LoadFunction(D); else { ungetc(c,D); break; }
}
}
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,代码如下:
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);
}
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
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");
}
}
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;
}
}
}
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;
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 做了哪些事儿,而这里是把它还原回去了。
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;
}
}
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;
}
}
到此,程序已经准备好运行了,开始虚拟机跑字节码了,也就是编译之后的内容了。