一个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;
}