版本整理日期:2011/3/31
本篇主要说明两个点:
1. 载入的IO流程,
2. lua内部调用函数流程
两个核心的函数
int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { LUAI_TRY(L, &lj, (*f)(L, ud); ); }
//先分析luaL_loadfile(L,fn),实际调用两个函数 #define luaL_dofile(L, fn) \ (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0))
在luaL_loadfile里,主要是几个结构体的传递初始化,因此一些语法上的逻辑将抛开,主要看结构体之间的IO组织和传递。
luaL_loadfile //获取输入IO ->getF(lua_Reader), LoadF) //表示传递这两个参数给下个调用的函数 Lua_load //构建ZIO ->ZIO(getF, LoadF) //填充一个IO结构 Lua_protectedparser //上下文中保护Sparser资源 ->Sparser(ZIO, name(函数名),f_parser) luaD_pcall //带p开头的,除了调用,还做了一些栈维护的清理工作 ->执行f_parser(Sparser) //f_parser是个剖析函数 luaD_rawrunprotected //公共的底层调用函数,这里调用f_parser f_parser ->Sparser->ZIO->getF(LoadF) //调用ZIO结构体,得到数据
整个过程就是
填充IO来源->调用->调用IO->解析读到的内容
如果有其他解析的需求,整个结构上只要修改ZIO的函数指针,和一些跳转的判断。来看具体代码实现
LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { //… //打开filename文件路径,存放到lf里 LoadF lf; status = lua_load(L, getF, &lf, lua_tostring(L, -1)); }
typedef struct LoadF { int extraline; //扩展的命令参数,略过 FILE *f; //打开文件指针 char buff[LUAL_BUFFERSIZE]; //读取的缓存 } LoadF;
LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *chunkname) { ZIO z; int status; lua_lock(L); if (!chunkname) chunkname = "?"; luaZ_init(L, &z, reader, data); status = luaD_protectedparser(L, &z, chunkname); lua_unlock(L); return status; }
struct Zio { size_t n; /* bytes still unread */ const char *p; /* current position in buffer */ //存储的是reader函数,也就是读文件 lua_Reader reader; //存储的是LoadF结构体 void* data; /* additional data */ lua_State *L; /* Lua state (for reader) */ };
int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) { /* struct SParser { /* data to `f_parser' */ ZIO *z; Mbuffer buff; /* buffer to be used by the scanner */ /* typedef struct Mbuffer { char *buffer; size_t n; size_t buffsize; } Mbuffer; */ //存放的是“LuaTest.lua” const char *name; }; */ struct SParser p; int status; p.z = z; p.name = name; luaZ_initbuffer(L, &p.buff); status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); luaZ_freebuffer(L, &p.buff); return status; }
static void f_parser (lua_State *L, void *ud) { int i; //Proto结构比较复杂,先略过 Proto *tf; // Closure *cl; struct SParser *p = cast(struct SParser *, ud); int c = luaZ_lookahead(p->z); luaC_checkGC(L); //按这里的调试是调用luaY_parser tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, &p->buff, p->name); cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); cl->l.p = tf; for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ cl->l.upvals[i] = luaF_newupval(L); setclvalue(L, L->top, cl); incr_top(L); }
int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) { int status; unsigned short oldnCcalls = L->nCcalls; ptrdiff_t old_ci = saveci(L, L->ci); lu_byte old_allowhooks = L->allowhook; ptrdiff_t old_errfunc = L->errfunc; L->errfunc = ef; /* func也就是f_parser u也就是Sparser luaD_rawrunprotected具体怎么执行先不关心,只知道会执行f_parser,并且参数是u */ status = luaD_rawrunprotected(L, func, u); if (status != 0) { /* an error occurred? */ //恢复栈,略过 } L->errfunc = old_errfunc; return status; }
跟踪f_parser
static void f_parser (lua_State *L, void *ud) { int i; Proto *tf; Closure *cl; struct SParser *p = cast(struct SParser *, ud); /* 核心会调用buff = z->reader(L, z->data, &size); 然后设置ZIO结构的 p和n 返回p的头一个字节,似乎判断是否预编译后的文件. 这里先调用luaY_parser, 这段函数代码就不具体贴了,做的工作大体是语法解析,构建函数等等 */ int c = luaZ_lookahead(p->z); luaC_checkGC(L); tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, &p->buff, p->name); cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); cl->l.p = tf; for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ cl->l.upvals[i] = luaF_newupval(L); setclvalue(L, L->top, cl); incr_top(L); }
接下来分析从lua文件中执行函数。如下代码为例
t={}
table.insert(t,100,13)
在ltablib.c中tinsert下断点,然后观察堆栈
LuaSrc.exe!tinsert(lua_State * L=0x00398c28) 行91 C
LuaSrc.exe!luaD_precall(lua_State * L=0x00398c28, lua_TValue * func=0x003950b8, int nresults=0) 行319 + 0x16 字节 C
LuaSrc.exe!luaV_execute(lua_State * L=0x00398c28, int nexeccalls=1) 行587 + 0x14 字节 C
LuaSrc.exe!luaD_call(lua_State * L=0x00398c28, lua_TValue * func=0x003950a8, int nResults=-1) 行377 + 0xb 字节 C
LuaSrc.exe!f_call(lua_State * L=0x00398c28, void * ud=0x0012fe68) 行800 + 0x16 字节 C
LuaSrc.exe!luaD_rawrunprotected(lua_State * L=0x00398c28, void (lua_State *, void *)* f=0x00420020, void * ud=0x0012fe68) 行118 + 0x1f 字节 C
LuaSrc.exe!luaD_pcall(lua_State * L=0x00398c28, void (lua_State *, void *)* func=0x00420020, void * u=0x0012fe68, int old_top=16, int ef=0) 行463 + 0x11 字节 C
> LuaSrc.exe!lua_pcall(lua_State * L=0x00398c28, int nargs=0, int nresults=-1, int errfunc=0) 行821 + 0x20 字节 C
LuaSrc.exe!wmain(int argc=1, wchar_t * * argv=0x00393250) 行55 + 0x29 字节 C++
LuaSrc.exe!__tmainCRTStartup() 行583 + 0x19 字节 C
LuaSrc.exe!wmainCRTStartup() 行403 C
主要把LUA里调用函数的一个规律总结出来(无关代码先剔除)
LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { /* struct CallS { /* data to `f_call' */ StkId func; int nresults; }; */ struct CallS c; //…其他代码省略 //取到要调用的函数体 c.func = L->top - (nargs+1); /* function to be called */ c.nresults = nresults; /* 对ud的一个调用 static void f_call (lua_State *L, void *ud) { struct CallS *c = cast(struct CallS *, ud); luaD_call(L, c->func, c->nresults); } */ status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); return status; }
往下走luaD_pcall
int luaD_pcall (lua_State *L, Pfunc func, void *u, ptrdiff_t old_top, ptrdiff_t ef) { //pcall,主要工作是一些堆栈保护,然后调用luaD_rawrunprotected status = luaD_rawrunprotected(L, func, u); return status; }
往下走luaD_rawrunprotected
int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { //先设置一些异常的跳转点,然后调用 LUAI_TRY(L, &lj, (*f)(L, ud); ); return lj.status; }
然后就是调用f_call,f_call又调用luaD->pcall,接着走luaD->pcall
void luaD_call (lua_State *L, StkId func, int nResults) { // luaD_precall做了什么先不关心,走luaV_execute if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ luaV_execute(L, 1); /* call it */ }
luaV_execute是很根据虚拟机字节码跳转的函数
void luaV_execute (lua_State *L, int nexeccalls) { const Instruction *pc; //`savedpc' of current function pc = L->savedpc; const Instruction i = *pc++; StkId ra; //取出调用的函数 ra = RA(i); case OP_CALL: { int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) L->top = ra+b; /* else previous instruction set top */ L->savedpc = pc; //终于快到头了 switch (luaD_precall(L, ra, nresults)) { } } }
往下走luaD_precall
int luaD_precall (lua_State *L, StkId func, int nresults) { LClosure *cl; // #define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) cl = &clvalue(func)->l; /* #define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) #define curr_func(L) (clvalue(L->ci->func)) */ n = (*curr_func(L)->c.f)(L); /* do the actual call */ }
接着调用到最终函数tinsert (lua_State *L)