Lua执行字节码的过程介绍

前面一篇文章中介绍了lua给下面代码生成最终的字节码的整个过程,这次我们来看看lua vm执行这些字节码的过程。

1 foo = "bar" 2 local a, b = "a", "b" 3 foo = a

生成的字节码如下所示:

Lua执行字节码的过程介绍

之前lua是在luaY_parser函数(入口)中完成了lua脚本的解析生成字节码的整个过程的,在生成了main func(过程见“Lua解析赋值类型代码的过程“)后luaY_parser会返回一个Proto结构体指针tf,Proto结构将描述整个main func的所有信息。

1 //如果此字符是LUA_SIGNATURE中的第一个字符说明文件内容是预编译好的文件内容,因此利用函数luaU_undump来加载一个预编译后的代码块 2 //否则是未编译的脚本源码,利用luaY_parser来对源码进行parse 3 tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, 4 &p->buff, p->name); 5 cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); 6 cl->l.p = tf; 7 for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ 8 cl->l.upvals[i] = luaF_newupval(L); 9 setclvalue(L, L->top, cl); 10 incr_top(L);

接下来第5行,函数luaF_newLclosure生成了一个Closure结构体来表示lua的closure,然后下一行将Proto结构体地址传给cl保存,接下来的循环里cl的upvalue数组记录下main func中的upvalue,然后setclvalue函数将cl放入到lua stack的栈顶上,incr_top将栈顶L->top加一。此时lua stack的顶部存放了包裹了main func的closure结构体,下面lua将会调用lua_pcall函数来执行这个closure了,也即vm加载整个生成的字节码并加以解释。

 

1 LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { 2 struct CallS c; 3 //... ... 4 c.func = L->top - (nargs+1); /* function to be called */ 5 c.nresults = nresults; 6 status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); 7 //... ... 8 } 9 /* 10 ** Execute a protected call. 11 */ 12 struct CallS { /* data to `f_call' */ 13 StkId func; 14 int nresults; 15 };

首先第4行根据要执行的函数参数数量和L->top的值来算出function在lua stack中的位置并将其保存到CallS结构体中,其中CallS结构体中的StkId类型为stack下标类型。接着第6行将c和f_call函数一起传入luaD_pcall函数中,luaD_pcall函数执行一些标志的设置后调用函数luaD_rawrunprotected,函数luaD_rawrunprotected内部调用f_call并将c作为其参数传入。如下所示:

1 static void f_call (lua_State *L, void *ud) { 2 struct CallS *c = cast(struct CallS *, ud); 3 luaD_call(L, c->func, c->nresults); 4 }

在luaD_call中首先判断lua此时是否到达了函数调用层次的最大值,超过这报错否则判断要执行的函数是不是lua function,是的话就调用luaV_execute函数来运行vm执行字节码。

1 void luaD_call (lua_State *L, StkId func, int nResults) { 2 if (++L->nCcalls >= LUAI_MAXCCALLS) { 3 if (L->nCcalls == LUAI_MAXCCALLS) 4 luaG_runerror(L, "C stack overflow"); 5 else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) 6 luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ 7 } 8 if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ 9 luaV_execute(L, 1); /* call it */ 10 L->nCcalls--; 11 luaC_checkGC(L); 12 }

luaV_execute函数是vm执行字节码的核心过程,整个函数约有400行代码,由于整个过程分支太多,我们只讲解示例中的字节码解析过程。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d702bc209ba3b84e32a8a5e32c509e70.html