我们来看看lua vm在解析下面源码并生成bytecode时的整个过程:
1 foo = "bar" 2 local a, b = "a", "b" 3 foo = a
首先我们先使用ChunkySpy这个工具来看看vm最终会具体生成什么样的vm instructions
在这里,开头为[数字]的行是vm真正生成的字节码,我们看到一共生成了六行字节码。首先loadk将常量表中下标为1的常量即"bar"赋给寄存器0;然后setglobal将寄存器0的内容赋给全局变量表中下标为0的全局变量即foo;loadk再将"a"和"b"分别赋值给了寄存器0、1,在这里寄存器0和1分别表示当前函数的local变量即变量a和b;最后setglobal将变量a的值赋给了全局变量foo;最后一个return01是vm在每一个chunk最后都会生成了,并没有什么用。现在应该比较清除的了解了lua vm生成的字节码的含义了,接下来我们看看vm是怎样且为什么生成这些个字节码的。
当我们用luaL_dofile函数执行这个lua脚本源码时会有两个阶段,第一个是将脚本加载进内存,分词解析并生成字节码并将其整个包裹为main chunk放于lua stack栈顶,第二是调用lua_pcall执行这个chunk,这里我们只会分析第一个过程。
前面几篇文章说了,当dofile时会跑到一个叫做luaY_parser的函数中,
1 Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { 2 struct LexState lexstate; 3 struct FuncState funcstate; 4 -- ... ... 5 funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ 6 luaX_next(&lexstate); /* read first token */ 7 chunk(&lexstate); 8 -- ... ... 9 return funcstate.f; 10 }
函数luaY_parser前面两行定义了LexState和FuncState结构体变量,其中LexState不仅用于保存当前的词法分析状态信息,而且也保存了整个编译系统的全局状态,FuncState结构体来保存当前函数编译的状态数据。在lua源码中都会有一个全局的函数执行体,即为main func,在开始解析的时候当前的函数必然是main func函数,此时第三行的funcstate表示了这个函数的状态,由于lua规定这个函数必然会接收不定参数因此第五行将is_vararg标识设为VARARG_ISVARARG。接着第六行luaX_next解析文件流分离出第一个token,将其保存在lexstate的t成员中,此时t为“foo”全局变量。接着调用了chunk函数,这里开始了递归下降解析的全部过程:
1 static void chunk (LexState *ls) { 2 /* chunk -> { stat [`;'] } */ 3 int islast = 0; 4 enterlevel(ls); 5 while (!islast && !block_follow(ls->t.token)) { 6 islast = statement(ls);//递归下降点 7 testnext(ls, ';'); 8 lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && 9 ls->fs->freereg >= ls->fs->nactvar); 10 ls->fs->freereg = ls->fs->nactvar; /* free registers */ 11 } 12 leavelevel(ls); 13 }
lua是有作用域层次概念的,因此当进入一个层次时会调用enterlevel函数,离开当前层次则会调用leavelevel函数。首先进入while循环,当前token为“foo”,这既不是终结标志也不是一个block开始的词素,因此会进入statement函数,statement函数主体是一个长长的switch...case...代码结构,根据第一个token进入不同的调用解析分支。在我们这个例子中会进入default分支:
1 static int statement (LexState *ls) { 2 -- ... ... 3 switch (ls->t.token) { 4 case TK_IF: { /* stat -> ifstat */ 5 ifstat(ls, line); 6 return 0; 7 } 8 case TK_WHILE: { /* stat -> whilestat */ 9 whilestat(ls, line); 10 return 0; 11 } 12 -- ... ... 13 default: { 14 exprstat(ls); 15 return 0; /* to avoid warnings */ 16 } 17 } 18 }
进入exprstate函数: