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

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

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

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

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

  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中读取,右边的同理。

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

  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上跳转,代码如下

  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字段,然后判断里面是否函数,如果是则进入调用。代码如下:

  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处,代码如下。

  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++函数处。代码如下:

  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的手法,但是一切还是以实际需求来进行选择运用。不一定一切非要元表的思维来实现

[转]lua元表代码分析的更多相关文章

  1. lighttpd与fastcgi+cgilua原理、代码分析与安装

    原理 http://www.cnblogs.com/skynet/p/4173450.html 快速通用网关接口(Fast Common Gateway Interface/FastCGI)是通用网关 ...

  2. [转]LUA元表

    lua元表和元方法 <lua程序设计> 13章 读书笔记 lua中每个值都有一个元表,talble和userdata可以有各自独立的元表,而其它类型的值则共享其类型所属的单一元表.lua在 ...

  3. Step By Step(Lua元表与元方法)

    Step By Step(Lua元表与元方法) Lua中提供的元表是用于帮助Lua数据变量完成某些非预定义功能的个性化行为,如两个table的相加.假设a和b都是table,通过元表可以定义如何计算表 ...

  4. Android代码分析工具lint学习

    1 lint简介 1.1 概述 lint是随Android SDK自带的一个静态代码分析工具.它用来对Android工程的源文件进行检查,找出在正确性.安全.性能.可使用性.可访问性及国际化等方面可能 ...

  5. pmd静态代码分析

    在正式进入测试之前,进行一定的静态代码分析及code review对代码质量及系统提高是有帮助的,以上为数据证明 Pmd 它是一个基于静态规则集的Java源码分析器,它可以识别出潜在的如下问题:– 可 ...

  6. [Asp.net 5] DependencyInjection项目代码分析-目录

    微软DI文章系列如下所示: [Asp.net 5] DependencyInjection项目代码分析 [Asp.net 5] DependencyInjection项目代码分析2-Autofac [ ...

  7. [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(5)(IEnumerable<>补充)

    Asp.net 5的依赖注入注入系列可以参考链接: [Asp.net 5] DependencyInjection项目代码分析-目录 我们在之前讲微软的实现时,对于OpenIEnumerableSer ...

  8. 完整全面的Java资源库(包括构建、操作、代码分析、编译器、数据库、社区等等)

    构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置进行构建.Maven优于Apache Ant.后者采用了一种过程化 ...

  9. STM32启动代码分析 IAR 比较好

    stm32启动代码分析 (2012-06-12 09:43:31) 转载▼     最近开始使用ST的stm32w108芯片(也是一款zigbee芯片).开始看他的启动代码看的晕晕呼呼呼的. 还好在c ...

随机推荐

  1. spring cloud服务发现注解之@EnableDiscoveryClient与@EnableEurekaClient区别

    在使用服务发现的时候有两种注解, 一种为@EnableDiscoveryClient, 一种为@EnableEurekaClient, 用法上基本一致,下文是从stackoverflow上面找到的对这 ...

  2. python中生成器

    1.简介 通过列表生成式,我们可以直接创建一个列表,但是受到内存的限制,列表容量肯定是有限的. 如果列表元素可以按照某种算法推算出来,那我们是否可以在循环的过程中不断推算出后续的元素呢? 在Pytho ...

  3. python中sys.argv[]的使用

    sys.argv[]主要用于程序从外部获取参数.其参数个数可以是多个,组建成一个列表(list). 几个简单示例如下: fun_test.py: #!/usr/bin/env python # -*- ...

  4. npm常用功能

    1.   npm -v在命令行中输入该代码,可以查看npm当前版本号 2.安装依赖包2.1  npm install <name>先使用cd命令跳转到需要安装模块的目录,在该目录下执行np ...

  5. git相关知识(github,idea等的配置)

    本地git提交文件到github上: 1.在github上创建项目 2.使用git clone https://github.com/xxxxxxx/xxxxx.git克隆到本地 3.编辑项目 4.g ...

  6. zzw原创_expdp及impdp中的exclude及include参数的那点事

    zzw原创:转载请注明出处 在oracle的expdp 及imdpd命令中,exclude及include参数还是有一些要注意的地方,特别是涉及选择性条件时. 一.通用 1.exclude及inclu ...

  7. drozer安装使用教程(Windows)

    drozer和adb一样,又不是新出的工具,本不该出了这么久还要由我这样半懂不懂的再写篇东西了.但是还是一样每次使用都得百度和筛选半天,所以记下来算给自己看.以后看到我还写些老掉牙的东西都是这个原因, ...

  8. memory prefix inter,intra,intro,iso out 5

    1● inter 在~之间:相互   2● intra 3● iso 等 同   4● intro 向内,在内,内部  

  9. matlab画图变粗脚本

    http://blog.sina.com.cn/s/blog_708637950100uag0.html figure_FontSize=18;set(get(gca,'XLabel'),'FontS ...

  10. 怎么样才是设计功能函数的好思路(javascript)?

    在js里面,对于函数的调用,实际上也是也是面向对象的思路,于是写好js函数,也是考核面向对象设计的能力,同时也必须考虑到如何实现高内聚和低耦合,拿一个例子来说,现在的需求是这样的,实现个投资进度框,就 ...