LUA CallInfo结构,StkId结构解析

StkId结构:

typedef union StackValue {
  TValue val;
} StackValue;

typedef StackValue *StkId;

StkId仅为一个正常的LUA变量

CallInfo结构:

typedef struct CallInfo {
  StkId func;
  StkId	top;
  struct CallInfo *previous, *next;
  union {
    struct {
      const Instruction *savedpc;
      volatile l_signalT trap;
      int nextraargs;
    } l;
    struct {
      lua_KFunction k;
      ptrdiff_t old_errfunc;
      lua_KContext ctx;
    } c;
  } u;
  union {
    int funcidx;
    int nyield;
    struct {
      unsigned short ftransfer;
      unsigned short ntransfer;
    } transferinfo;
  } u2;
  short nresults;
  unsigned short callstatus;
} CallInfo;

:CallInfo最主要的作用是记录一个函数调用涉及到的栈引用.每个函数的栈空间并不是一个独立的数组,而是所有函数公用一个共同的栈数据结构实例,func是与调用关联的函数对象,这个对象在线程的栈中,而top则是调用的栈顶,[func, top]就是这个调用使用的栈范围。
之前的LUA版本适用的数据结构如下图所示(由于未找到5.3版本的图):
栈示意图
可见函数对象在栈中,且CallInfo.func 为函数对象在栈中的序列号,栈的使用范围被限制在[func, top]中,且第一个参数序列号为1逐级向上

调用链:Lua在调用每个函数时,都会生成一个CallInfo,并将它们链接成一个双向链表。通过遍历这个链表,我们就可以知道整个调用链 & 调用关系,previous和next会使CallInfo形成一个双向链表,lua_State->ci则记录着当前的调用,这个链表可以用下图表示:
双向链表
另外3个UNION结构暂放,先看此结构产生的相关环境
负责生成并填写此结构的函数为:luaD_call(ido.c):

void luaD_call (lua_State *L, StkId func, int nresults) {
  lua_CFunction f;
 retry:
  switch (ttypetag(s2v(func))) {
    case LUA_VCCL:  /* C closure */
      f = clCvalue(s2v(func))->f;
      goto Cfunc;
    case LUA_VLCF:  /* light C function */
      f = fvalue(s2v(func));
     Cfunc: {
		...
    }
    case LUA_VLCL: {  /* Lua function */
		...
    }
    default: {  /* not a function */
      checkstackGCp(L, 1, func);  /* space for metamethod */
      luaD_tryfuncTM(L, func);  /* try to get '__call' metamethod */
      goto retry;  /* try again with metamethod */
    }
  }
}

函数主体通过switch case调度,其中作为区分调度类别的关键变量:ttypetag(s2v(func))

ttypetag(s2v(func))--------->withvariant(rawtt(s2v(func)))
  1. func变量为StkId类型,其中只有一个成员变量为:TValue val
  2. #define s2v(o) (&(o)->val),即将func的真实TValue取出
  3. #define rawtt(o) ((o)->tt_),即将func变量的TValue成员的类型取出
  4. #define withvariant(t) ((t) & 0x3F),将成员类型与0x3F进行与运算

通过分析case的具体值可以得知各类型的值是多少,其中

LUA_VCCL:/* C closure */0x26(0x66 & 0x3F)
LUA_VLCF:/* light C function */0x16(0x16 & 0x3F)
LUA_VLCL:/* Lua function */0x6

对于C closure函数:

f = clCvalue(s2v(func))->f;
#define clCvalue(o)	check_exp(ttisCclosure(o), gco2ccl(val_(o).gc))
且:ttisCclosure(o)((o)->tt_) == 0x66
且:gco2ccl(val_(o).gc):取出func的GCObject成员用以初始化GCUnion结构体,并去除其中Closure类型的cl,再将cl中的CClosure c取出
union GCUnion {
  GCObject gc;  /* common header */
  struct TString ts;
  struct Udata u;
  union Closure cl;
  struct Table h;
  struct Proto p;
  struct lua_State th;  /* thread */
  struct UpVal upv;
};check_exp(true/false, CClosure c) == CClosure c,因此整体含义为初始化对于的CClosure c,
并将其中的lua_CFunction f成员取出赋值给f,然后执行Cfunc标签

对于light C函数:

f = fvalue(s2v(func)) 展开后为 f = ((((&(func)->val))->value_).f);
即直接取出func中的lua_CFunction f成员赋值给f,然后执行Cfunc函数

上述两个case均为赋值f后转向执行Cfunc标签,其源码如下:

int n;  /* number of returns */
CallInfo *ci;
checkstackGCp(L, LUA_MINSTACK, func);  /* ensure minimum stack size */
L->ci = ci = next_ci(L);
ci->nresults = nresults;
ci->callstatus = CIST_C;
ci->top = L->top + LUA_MINSTACK;
ci->func = func;
lua_assert(ci->top <= L->stack_last);
if (L->hookmask & LUA_MASKCALL) {
  int narg = cast_int(L->top - func) - 1;
  luaD_hook(L, LUA_HOOKCALL, -1, 1, narg);
}
lua_unlock(L);
n = (*f)(L);  /* do the actual call */
lua_lock(L);
api_checknelems(L, n);
luaD_poscall(L, ci, n);
break;

创建CallInfo结构体后检查栈空间是否大于LUA_MINSTACK
填充ci结构体,L->ci = ci = (L->ci->next ? L->ci->next : luaE_extendCI(L)),如果存在nextci则直接填充,否则就新生成一个ci节点
进行其他结构体填充 & 检查栈越界问题
参考:【深入Lua:调用信息】深入Lua:调用信息.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值