标签:
这两个函数的定义都位于 ldo.c 中,看看这两个函数都做了什么事儿?
先来看一下 lua_dofile 执行文件
LUA_API int lua_dofile (lua_State *L, const char *filename) {
int status = parse_file(L, filename);
if (status == 0) /* parse OK? */
status = lua_call(L, 0, LUA_MULTRET); /* call main */
return status;
}
先解析文件,如果解析无误,则调用。
由函数名字及下面的调用我们可以猜出,parse_file 应该是做的语法解析。
static int parse_file (lua_State *L, const char *filename) {
ZIO z;
int status;
int bin; /* flag for file mode */
int c; /* look ahead char */
FILE *f = (filename == NULL) ? stdin : fopen(filename, "r");
if (f == NULL) return LUA_ERRFILE; /* unable to open file */
c = fgetc(f);
ungetc(c, f);
bin = (c == ID_CHUNK);
if (bin && f != stdin) {
f = freopen(filename, "rb", f); /* set binary mode */
if (f == NULL) return LUA_ERRFILE; /* unable to reopen file */
}
lua_pushstring(L, "@");
lua_pushstring(L, (filename == NULL) ? "(stdin)" : filename);
lua_concat(L, 2);
c = lua_gettop(L);
filename = lua_tostring(L, c); /* filename = ‘@‘..filename */
luaZ_Fopen(&z, f, filename);
status = protectedparser(L, &z, bin);
lua_remove(L, c); /* remove `filename‘ from the stack */
if (f != stdin)
fclose(f);
return status;
}
先根据文件名来判断输入的是什么?
如果文件名为空,则从标准输入读取。
否则从文件名读取。
取得文件的第一个字符,如果是 ID_CHUNK 的话,表示文件是一个已经编译好的 Lua 字节码文件。
bin 标志位就是用来标识这个文件是否为 Lua 字节码文件。
后面对 filename 进行编码,前面添加 ‘@‘ 符号。
luaZ_Fopen 打开缓冲区。
protectedparser 解析。
先不看 protectedparser 里做什么了。
先看下 lua_dostring,因为最后也是调到了 protectedparser 身上。
LUA_API int lua_dostring (lua_State *L, const char *str) {
return lua_dobuffer(L, str, strlen(str), str);
}
lua_dostring 内部调用 lua_dobuffer 来实现。
可以看出这里调用 lua_dobuffer 时 str 即当 buff 参数,又当 name 参数。
LUA_API int lua_dobuffer (lua_State *L, const char *buff, size_t size, const char *name) {
int status = parse_buffer(L, buff, size, name);
if (status == 0) /* parse OK? */
status = lua_call(L, 0, LUA_MULTRET); /* call main */
return status;
}
同样,和文件类型,也是先语法解析。
解析无误,则调用。
static int parse_buffer (lua_State *L, const char *buff, size_t size,
const char *name) {
ZIO z;
if (!name) name = "?";
luaZ_mopen(&z, buff, size, name);
return protectedparser(L, &z, buff[0]==ID_CHUNK);
}
可以看到,在解析字符串 buffer 时,最后也是调到了 protectedparser 身上。
到 protectedparser 时,由于缓冲区 ZIO 的作用,已经没有文件或者字符串的区别了。
很不错的设计思想!
这时候,再来看看 protectedparser 。
static int protectedparser (lua_State *L, ZIO *z, int bin) {
struct ParserS p;
unsigned long old_blocks;
int status;
p.z = z; p.bin = bin;
/* before parsing, give a (good) chance to GC */
if (L->nblocks/8 >= L->GCthreshold/10)
luaC_collectgarbage(L);
old_blocks = L->nblocks;
status = luaD_runprotected(L, f_parser, &p);
if (status == 0) {
/* add new memory to threshold (as it probably will stay) */
L->GCthreshold += (L->nblocks - old_blocks);
}
else if (status == LUA_ERRRUN) /* an error occurred: correct error code */
status = LUA_ERRSYNTAX;
return status;
}
可以看到它里面调用了 f_parser 。
static void f_parser (lua_State *L, void *ud) {
struct ParserS *p = (struct ParserS *)ud;
Proto *tf = p->bin ? luaU_undump(L, p->z) : luaY_parser(L, p->z);
luaV_Lclosure(L, tf, 0);
}
f_parser 里调用 luaY_parser 来做具体的语法解析。
如果已经是字节码的文件,就不用语法解析了,直接 luaU_undump 字节码就好。
到这里,就差 lua_call 的执行了。
LUA_API int lua_call (lua_State *L, int nargs, int nresults) {
StkId func = L->top - (nargs+1); /* function to be called */
struct CallS c;
int status;
c.func = func; c.nresults = nresults;
status = luaD_runprotected(L, f_call, &c);
if (status != 0) /* an error occurred? */
L->top = func; /* remove parameters from the stack */
return status;
}
lua_call 里调用 f_call
static void f_call (lua_State *L, void *ud) {
struct CallS *c = (struct CallS *)ud;
luaD_call(L, c->func, c->nresults);
}
f_call 里调用 luaD_call。
然后是 luaV_execute(L, cl, func+1));
这个就是虚拟机执行字节码指令了。
luaV_execute 就是个大大的 switch case,不贴代码了。
翻了下之前的博客,最早的时候有一个虚拟机执行的分析。
这里就不再重复一次这种体力劳动了。
力气应该花在更有意义的地方!
----------------------------------------
到目前为止的问题:
> 无!
----------------------------------------
Lua4.0 lua_dofile,lua_dostring
标签:
原文地址:http://my.oschina.net/xhan/blog/495452