lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


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