lua函数中的self的实现



一个lua的表函数要有类似C++类函数的this指针,在函数定义,和函数调用时都得使用冒号, 如

local tb = {scale=1}
function tb:add(a, b)
    return self.scale + a + b
end

调用方式: local result = tb:add(1, 3)

来看看函数声明的词法分析的过程, 遇到function token会进行funcstat的分析:

static void funcstat (LexState *ls, int line) {
  /* funcstat -> FUNCTION funcname body */
  int needself;
  expdesc v, b;
  luaX_next(ls);  /* skip FUNCTION */
  needself = funcname(ls, &v);
  body(ls, &b, needself, line);
  luaK_storevar(ls->fs, &v, &b);
  luaK_fixline(ls->fs, line);  /* definition `happens' in the first line */
}


关键点为funcname函数,它用来取出函数名,并判定函数名是否有冒号,返回1为需要self

static int funcname (LexState *ls, expdesc *v) {
  /* funcname -> NAME {field} [`:' NAME] */
  int needself = 0;
  singlevar(ls, v);
  while (ls->t.token == '.')
    field(ls, v);
  if (ls->t.token == ':') {
    needself = 1;
    field(ls, v);
  }
  return needself;
}


标志是否需要self变量的needself传入给了body函数

static void body (LexState *ls, expdesc *e, int needself, int line) {
  /* body ->  `(' parlist `)' chunk END */
  FuncState new_fs;
  open_func(ls, &new_fs);
  new_fs.f->linedefined = line;
  checknext(ls, '(');
  if (needself) {
    new_localvarliteral(ls, "self", 0); // 分配self变量
    adjustlocalvars(ls, 1);
  }

  parlist(ls);
  checknext(ls, ')');
  chunk(ls);
  new_fs.f->lastlinedefined = ls->linenumber;
  check_match(ls, TK_END, TK_FUNCTION, line);
  close_func(ls);
  pushclosure(ls, &new_fs, e);
}


body中根据needself标志决定是否把变量列表的第一个位置预留给self。parlist函数就解析真正的参数并列在self之后。词法分析过程需要保证一个函数内所有指令中使用的变量的栈位置的正确, 比如函数里头有一个表达式用到了self变量,在生成这条指令时, 变量的位置就会被使用。

 

以上只是为self预留了位置,那么self即表自身是在什么时候被压入第一个位置呢?

在解析表函数调用的时候(lparse.c primaryexp函数第711行), 会压入一条self指令,有了这条指令,在运行期间就可以在函数调入前对self变量做处理

 

      case ':': {  /* `:' NAME funcargs */
        expdesc key;
        luaX_next(ls);
        checkname(ls, &key);
        luaK_self(fs, v, &key);
        funcargs(ls, v);
        break;
      }


void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
  int func;
  luaK_exp2anyreg(fs, e);
  freeexp(fs, e);
  func = fs->freereg;
  luaK_reserveregs(fs, 2);
  luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key));
  freeexp(fs, key);
  e->u.s.info = func;
  e->k = VNONRELOC;
}


self指令把函数的位置, 表的位置,函数key的位置都做为操作数。 在运行时就根据这些操作做初始化

 

      case OP_SELF: {
        StkId rb = RB(i);
        setobjs2s(L, ra+1, rb); // 把table赋值到self的位置, 放到ra + 1处
        Protect(luaV_gettable(L, rb, RKC(i), ra)); // 从表中获取函数 放到ra处
        continue;
      }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值