lua虚拟机函数调用指令OP_CALL、OP_RETURN
来源:互联网 发布:ubuntu 安装svnserver 编辑:程序博客网 时间:2024/06/06 12:43
lua虚拟机中有3种函数,lua闭包,c函数,c闭包
#define LUA_TLCL (LUA_TFUNCTION | (0 << 4)) /* Lua closure */#define LUA_TLCF (LUA_TFUNCTION | (1 << 4)) /* light C function */#define LUA_TCCL (LUA_TFUNCTION | (2 << 4)) /* C closure */
c函数和c闭包的实现和lua闭包的实现略有不同
vmcase(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 */ if (luaD_precall(L, ra, nresults)) { /* C function? */ if (nresults >= 0) L->top = ci->top; /* adjust results */ Protect((void)0); /* update 'base' */ } else { /* Lua function */ ci = L->ci; goto newframe; /* restart luaV_execute over new Lua function */ } vmbreak; }
因为lua闭包的实现需要return指令,指令OP_RETURN完成了后半部的实现。
先看看c函数和c闭包的实现,lua中的函数可以有多个返回值,所有这里先计算出返回值的个数,然后调用luaD_precall实现函数调用,调用结束后需要调整L->top的值,因为ci->top是为c函数调用保留的栈空间标记位,所有这里L->top调整到ci->top处,这样就不会侵占为c函数预留的占空间。
int luaD_precall (lua_State *L, StkId func, int nresults) { lua_CFunction f; CallInfo *ci; switch (ttype(func)) { case LUA_TCCL: /* C closure */ f = clCvalue(func)->f; goto Cfunc; case LUA_TLCF: /* light C function */ f = fvalue(func); Cfunc: { int n; /* number of returns */ checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; ci->func = func; ci->top = L->top + LUA_MINSTACK; lua_assert(ci->top <= L->stack_last); ci->callstatus = 0; if (L->hookmask & LUA_MASKCALL) luaD_hook(L, LUA_HOOKCALL, -1); lua_unlock(L); n = (*f)(L); /* do the actual call */ lua_lock(L); api_checknelems(L, n); luaD_poscall(L, ci, L->top - n, n); return 1; } 。。。}
c函数和c闭包大部分处理一样,只是函数的来源对象不一样,(c闭包需要存储更多的数据)。首先要为调用栈预留足够的栈空间LUA_MINSTACK,然后记录函数调用栈ci = next_ci(L);再然后存储一些必要的值后调用c函数n = (*f)(L);调用结束后使用luaD_poscall调整返回值和函数调用栈。
lua闭包的调用分两步实现
第一步是OP_CALL
vmcase(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 */ if (luaD_precall(L, ra, nresults)) { /* C function? */ 。。。 } else { /* Lua function */ ci = L->ci; goto newframe; /* restart luaV_execute over new Lua function */ } vmbreak;
和c函数调用实现一样,调用luaD_precall
int luaD_precall (lua_State *L, StkId func, int nresults) { lua_CFunction f; CallInfo *ci; switch (ttype(func)) { 。。。 case LUA_TLCL: { /* Lua function: prepare its call */ StkId base; Proto *p = clLvalue(func)->p; int n = cast_int(L->top - func) - 1; /* number of real arguments */ int fsize = p->maxstacksize; /* frame size */ checkstackp(L, fsize, func); if (p->is_vararg != 1) { /* do not use vararg? */ for (; n < p->numparams; n++) setnilvalue(L->top++); /* complete missing arguments */ base = func + 1; } else base = adjust_varargs(L, p, n); ci = next_ci(L); /* now 'enter' new function */ ci->nresults = nresults; ci->func = func; ci->u.l.base = base; L->top = ci->top = base + fsize; lua_assert(ci->top <= L->stack_last); ci->u.l.savedpc = p->code; /* starting point */ ci->callstatus = CIST_LUA; if (L->hookmask & LUA_MASKCALL) callhook(L, ci); return 0; } 。。。}
除了记录一些基本数据和函数调用栈外,这里最重要的是改变了lua虚拟机的程序指令指针的值:ci->u.l.savedpc = p->code; /* starting point */
这个函数返回后,下一步就从ci->u.l.savedpc处开始执行。直到执行到OP_RETURN,这个函数才会返回:
vmcase(OP_RETURN) { int b = GETARG_B(i); if (cl->p->sizep > 0) luaF_close(L, base); b = luaD_poscall(L, ci, ra, (b != 0 ? b - 1 : cast_int(L->top - ra))); if (ci->callstatus & CIST_FRESH) /* local 'ci' still from callee */ return; /* external invocation: return */ else { /* invocation via reentry: continue execution */ ci = L->ci; if (b) L->top = ci->top; lua_assert(isLua(ci)); lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL); goto newframe; /* restart luaV_execute over new Lua function */ } }
这里主要的操作时调整返回值,恢复函数调用栈,恢复指令指针计数器,至此lua闭包执行结束。
0 0
- lua虚拟机函数调用指令OP_CALL、OP_RETURN
- Lua 虚拟机指令
- Lua虚拟机指令简介
- lua指令集与虚拟机
- Lua5.3 虚拟机指令分析(五)函数调用
- [lua]C调用lua函数
- Lua源码分析 -- 虚拟机以及指令解释
- 调用Lua函数
- c++调用lua函数
- 调用Lua函数
- Lua调用C函数
- 调用Lua函数
- lua 调用c函数
- Lua调用C++函数
- Lua文件函数调用
- lua的函数调用
- Lua调用c++函数
- Lua调用C函数
- jmeter使用badboy录制脚本
- 用js打印直角三角形杨辉三角
- HDU 5703 Desert(水题)
- Linux内核---28.yaffs2的垃圾回收机制
- 如何配置网卡
- lua虚拟机函数调用指令OP_CALL、OP_RETURN
- Linux内核---29.yaffs2的ECC
- linux下使用tar命令
- PopupWindow
- 工程/文件目录,相对路径
- Android Binder机制介绍
- Linux内核---30.触摸屏驱动分析
- 面试题27:二叉搜索树和双向链表
- Linux内核---31.按键驱动分析(未完成)