zoukankan      html  css  js  c++  java
  • [转]lua元表代码分析

    http://lin-style.iteye.com/blog/1012138
     

    版本整理日期:2011/4/21

    元表其实就是可以让你HOOK掉一些操作的一张表。

    表的定义在ltm.h/c的文件里。对元表的调用在lvm文件里。

    来看看是怎么hook的。首先定义了一堆的枚举

    Cpp代码  
    1. typedef enum {  
    2.   TM_INDEX,  
    3.   TM_NEWINDEX,  
    4.   TM_GC,  
    5.   TM_MODE,  
    6.   TM_EQ,  /* last tag method with `fast' access */  
    7.   TM_ADD,  
    8.   TM_SUB,  
    9.   TM_MUL,  
    10.   TM_DIV,  
    11.   TM_MOD,  
    12.   TM_POW,  
    13.   TM_UNM,  
    14.   TM_LEN,  
    15.   TM_LT,  
    16.   TM_LE,  
    17.   TM_CONCAT,  
    18.   TM_CALL,  
    19.   TM_N      /* number of elements in the enum */  
    20. }  

    除了TM_INDEX和TM_NEWINDEX外,其他的都是实现定义好的操作符。也就是说如果你要改变s1+s2这样的行为,只要直接设置TM_ADD的函数即可。TM_INDEX和TM_NEWINDEX表示读取和等于。比如s[s’]=x[‘x’’]中,左边的[s’]如果没有这个字段则从TM_NEWINDEX中读取,右边的同理。

    元表可以存在于这些对象上:

    Cpp代码  
    1. const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {  
    2.   Table *mt;  
    3.   switch (ttype(o)) {  
    4.     case LUA_TTABLE:         //表  
    5.       mt = hvalue(o)->metatable;  
    6.       break;  
    7.     case LUA_TUSERDATA:      //自定义对象  
    8.       mt = uvalue(o)->metatable;  
    9.       break;  
    10.     default:                 //全局  
    11.       mt = G(L)->mt[ttype(o)];  
    12.   }  
    13.   return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject);  
    14. }  

    在lua源码里,对元表的访问其实也是很简单的:

    我们直接从虚拟机代码开始跟起,通过元表存在的对象类型上,会从OP_GETTABLE上跳转,代码如下

    Cpp代码  
    1. case OP_GETTABLE: {  
    2.         Protect(luaV_gettable(L, RB(i), RKC(i), ra));  
    3.         continue;  
    4.       }  

    在luaV_gettable中,会先扫描是否有RKC(i)的值。即上文提到的s[s’]=x[‘x’’]中的[s’]。如果有则返回,直接读取;否则扫描TM_INDEX字段,然后判断里面是否函数,如果是则进入调用。代码如下:

    Cpp代码  
    1. void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {  
    2.   int loop;  
    3.   for (loop = 0; loop < MAXTAGLOOP; loop++) {  
    4.     const TValue *tm;  
    5.     if (ttistable(t)) {  /* `t' is a table? */  
    6.       Table *h = hvalue(t);  
    7.       const TValue *res = luaH_get(h, key); /* do a primitive get */  
    8.       if (!ttisnil(res) ||  /* result is no nil? */  
    9.           (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */  
    10.         setobj2s(L, val, res);  
    11.         return;  
    12.       }  
    13.       /* else will try the tag method */  
    14.     }  
    15.     else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))  
    16.       luaG_typeerror(L, t, "index");  
    17. if (ttisfunction(tm)) {  
    18.   //这里是我们的跳转点  
    19.       callTMres(L, val, tm, t, key);  
    20.       return;  
    21.     }  
    22.     t = tm;  /* else repeat with `tm' */   
    23.   }  
    24.   luaG_runerror(L, "loop in gettable");  
    25. }  

     继callTMres(L, val, tm, t, key);往下跟,先把要调用的函数(tm)压入,接着是lua固用的调用代码流程(如果你熟悉前篇的话),最后又来到虚拟机的OP_CALL处,代码如下。

    Cpp代码  
    1. case OP_CALL: {  
    2.    int b = GETARG_B(i);  
    3.    int nresults = GETARG_C(i) - 1;  
    4.    if (b != 0) L->top = ra+b;  /* else previous instruction set top */  
    5.    L->savedpc = pc;  
    6.  //这里是我们的进入点  
    7.    switch (luaD_precall(L, ra, nresults)) {  

    在熟悉的luaD_precall中,来到我们设定的C++函数处。代码如下:

    Cpp代码  
    1. int luaD_precall (lua_State *L, StkId func, int nresults) {  
    2.     n = (*curr_func(L)->c.f)(L);  /* do the actual call */  
    3. }  

     元表虽然给我们提供了一种hook的手法,但是一切还是以实际需求来进行选择运用。不一定一切非要元表的思维来实现

  • 相关阅读:
    bzoj 1071: [SCOI2007]组队
    bzoj 4872: [Shoi2017]分手是祝愿
    (python)循环中动态产生变量
    Python中的exec、eval的区别
    MATLAB中feval与eval的区别
    用intellij idea 写第一个Java程序
    Python词云的中文问题
    python里的apply,applymap和map的区别
    Python 正则表达式匹配小数
    字典的深拷贝与浅拷贝
  • 原文地址:https://www.cnblogs.com/freebird92/p/6760786.html
Copyright © 2011-2022 走看看