lua的metatable查找成员源码分析

来源:互联网 发布:淘宝助理上架教程 编辑:程序博客网 时间:2024/05/29 14:59
lua中如果访问的成员不存在,lua会继续在其对应的metatable中查找。lua中每种数据结构都有metatable,但是只有table和userdata有自己私有的metatable,剩下的数据结构有各自共享的metatable。
const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {  Table *mt;  switch (ttnov(o)) {    case LUA_TTABLE:      mt = hvalue(o)->metatable;      break;    case LUA_TUSERDATA:      mt = uvalue(o)->metatable;      break;    default:      mt = G(L)->mt[ttnov(o)];  }  return (mt ? luaH_getshortstr(mt, G(L)->tmname[event]) : luaO_nilobject);}
从最后一句的返回值可以看出来metatable必须要有一个叫"__index"的成员,否则metatable的成员查找链无法生效。从table中获取成员的入口函数:
LUA_API int lua_gettable (lua_State *L, int idx) {  StkId t;  lua_lock(L);  t = index2addr(L, idx);  luaV_gettable(L, t, L->top - 1, L->top - 1);  lua_unlock(L);  return ttnov(L->top - 1);}

直接将功能转给了luaV_gettable

#define luaV_gettable(L,t,k,v) { const TValue *slot; \  if (luaV_fastget(L,t,k,slot,luaH_get)) { setobj2s(L, v, slot); } \  else luaV_finishget(L,t,k,v,slot); }

这是一个宏定义,首先使用luaV_fastget尝试直接从table中获取成员,如果失败了,再调用luaV_finishget在metatable中查找成员

void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,                      const TValue *slot) {  int loop;  /* counter to avoid infinite loops */  const TValue *tm;  /* metamethod */  for (loop = 0; loop < MAXTAGLOOP; loop++) {    if (slot == NULL) {  /* 't' is not a table? */      lua_assert(!ttistable(t));      tm = luaT_gettmbyobj(L, t, TM_INDEX);      if (ttisnil(tm))        luaG_typeerror(L, t, "index");  /* no metamethod */      /* else will try the metamethod */    }    else {  /* 't' is a table */      lua_assert(ttisnil(slot));      tm = fasttm(L, hvalue(t)->metatable, TM_INDEX);  /* table's metamethod */      if (tm == NULL) {  /* no metamethod? */        setnilvalue(val);  /* result is nil */        return;      }      /* else will try the metamethod */    }    if (ttisfunction(tm)) {  /* is metamethod a function? */      luaT_callTM(L, tm, t, key, val, 1);  /* call it */      return;    }    t = tm;  /* else try to access 'tm[key]' */    if (luaV_fastget(L,t,key,slot,luaH_get)) {  /* fast track? */      setobj2s(L, val, slot);  /* done */      return;    }    /* else repeat (tail call 'luaV_finishget') */  }  luaG_runerror(L, "'__index' chain too long; possible loop");}

为了防止metatable循环嵌套,最多查找MAXTAGLOOP(2000)次,这里获取的tm是metatable的__index成员,如果tm是函数,则直接调用tm,如果是table则在其中查找,如果没有找到则设置t为tm重复这个过程,直到找到或者超过最大次数限制。

0 0
原创粉丝点击