[Date Prev][Date Next][Thread Prev][Thread Next]
[Date Index]
[Thread Index]
- Subject: A quick methods lookup patch
- From: Mark Hamburg <mark@...>
- Date: Wed, 25 Nov 2009 11:01:08 -0800
I had someone ask for a patch to do methods lookup. I'm afraid this won't be in the form of a patch file, but it's pretty straightforward to add a simple version of this:
In ltm.h, we add a TM_METHODS entry to the list of metamethods:
typedef enum {
TM_INDEX,
TM_NEWINDEX,
TM_METHODS, /* <<<< Methods patch */
TM_GC,
TM_MODE,
TM_EQ, /* last tag method with `fast' access */
TM_ADD,
TM_SUB,
TM_MUL,
TM_DIV,
TM_MOD,
TM_POW,
TM_UNM,
TM_LEN,
TM_LT,
TM_LE,
TM_CONCAT,
TM_CALL,
TM_N /* number of elements in the enum */
} TMS;
In ltm.c, we make the corresponding addition to give it a name (the only change is the addition of "__methods"):
void luaT_init (lua_State *L) {
static const char *const luaT_eventname[] = { /* ORDER TM */
"__index", "__newindex", "__methods", /* <<<< Methods patch */
"__gc", "__mode", "__eq",
"__add", "__sub", "__mul", "__div", "__mod",
"__pow", "__unm", "__len", "__lt", "__le",
"__concat", "__call"
};
int i;
for (i=0; i<TM_N; i++) {
G(L)->tmname[i] = luaS_new(L, luaT_eventname[i]);
luaS_fix(G(L)->tmname[i]); /* never collect these names */
}
}
In lvm.c, we change the OP_SELF entry to call luaV_getmethod rather than luaV_gettable:
case OP_SELF: {
StkId rb = RB(i);
setobjs2s(L, ra+1, rb);
Protect(luaV_getmethod(L, rb, RKC(i), ra)); /* <<<< Methods patch */
continue;
}
And we implement luaV_getmethod. This is basically a variation on luaV_gettable with code added to look for a __methods entry before looking for an __index entry. If you want to try other lookup semantics, this is the place to add code. For example, one could instead decide that the presence of a __methods metatable entry always overrides the lookup in the object possibly even preventing lookup within the object.
void luaV_getmethod (lua_State *L, const TValue *t, TValue *key, StkId val) {
int loop;
for (loop = 0; loop < MAXTAGLOOP; loop++) {
const TValue *tm;
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
const TValue *res = luaH_get(h, key); /* do a primitive get */
if (!ttisnil(res) || /* result is no nil? */
(((tm = fasttm(L, h->metatable, TM_METHODS)) == NULL) &&
((tm = fasttm(L, h->metatable, TM_INDEX)) == NULL))) { /* or no TM? */
setobj2s(L, val, res);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_METHODS)) &&
ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
luaG_typeerror(L, t, "invoke method");
if (ttisfunction(tm)) {
callTMres(L, val, tm, t, key);
return;
}
t = tm; /* else repeat with `tm' */
}
luaG_runerror(L, "loop in getmethod");
}
If you wanted to try semantics that could also change the target object, then you would need to do so in OP_SELF.
Mark