From 69e0240978fe86d032167711a9b6c7058db0c897 Mon Sep 17 00:00:00 2001
From: Roberto Ierusalimschy <roberto@inf.puc-rio.br>
Date: Mon, 10 Sep 2007 14:59:32 -0300
Subject: [PATCH] tables and strings respect __len metamethod

---
 src/blua/ltm.c |  4 ++--
 src/blua/ltm.h |  2 +-
 src/blua/lvm.c | 45 ++++++++++++++++++++++++++++-----------------
 3 files changed, 31 insertions(+), 20 deletions(-)

diff --git a/src/blua/ltm.c b/src/blua/ltm.c
index 031a2811e9..570bded697 100644
--- a/src/blua/ltm.c
+++ b/src/blua/ltm.c
@@ -31,9 +31,9 @@ void luaT_init (lua_State *L) {
   static const char *const luaT_eventname[] = {  /* ORDER TM */
     "__index", "__newindex",
     "__usedindex",
-    "__gc", "__mode", "__eq",
+    "__gc", "__mode", "__len", "__eq",
     "__add", "__sub", "__mul", "__div", "__mod",
-    "__pow", "__unm", "__len", "__lt", "__le",
+    "__pow", "__unm", "__lt", "__le",
     "__concat", "__call"
     ,"__strhook"
     ,"__and", "__or", "__xor", "__shl", "__shr", "__not"
diff --git a/src/blua/ltm.h b/src/blua/ltm.h
index 3d4ac8c0ec..3f2aa4a45a 100644
--- a/src/blua/ltm.h
+++ b/src/blua/ltm.h
@@ -21,6 +21,7 @@ typedef enum {
   TM_USEDINDEX,
   TM_GC,
   TM_MODE,
+  TM_LEN,
   TM_EQ,  /* last tag method with `fast' access */
   TM_ADD,
   TM_SUB,
@@ -29,7 +30,6 @@ typedef enum {
   TM_MOD,
   TM_POW,
   TM_UNM,
-  TM_LEN,
   TM_LT,
   TM_LE,
   TM_CONCAT,
diff --git a/src/blua/lvm.c b/src/blua/lvm.c
index 46a015c1eb..b74fef4ee3 100644
--- a/src/blua/lvm.c
+++ b/src/blua/lvm.c
@@ -311,6 +311,33 @@ void luaV_concat (lua_State *L, int total, int last) {
 }
 
 
+static void objlen (lua_State *L, StkId ra, TValue *rb) {
+  const TValue *tm;
+  switch (ttype(rb)) {
+    case LUA_TTABLE: {
+      Table *h = hvalue(rb);
+      tm = fasttm(L, h->metatable, TM_LEN);
+      if (tm) break;  /* metamethod? break switch to call it */
+      setnvalue(ra, cast_num(luaH_getn(h)));  /* else primitive len */
+      return;
+    }
+    case LUA_TSTRING: {
+      tm = fasttm(L, G(L)->mt[LUA_TSTRING], TM_LEN);
+      if (tm) break;  /* metamethod? break switch to call it */
+      setnvalue(ra, cast_num(tsvalue(rb)->len));
+      return;
+    }
+    default: {  /* try metamethod */
+      tm = luaT_gettmbyobj(L, rb, TM_LEN);
+      if (ttisnil(tm))  /* no metamethod? */
+        luaG_typeerror(L, rb, "get length of");
+      break;
+    }
+  }
+  callTMres(L, ra, tm, rb, luaO_nilobject);
+}
+
+
 static void Arith (lua_State *L, StkId ra, TValue *rb,
                    TValue *rc, TMS op) {
   TValue tempb, tempc;
@@ -568,23 +595,7 @@ void luaV_execute (lua_State *L, int nexeccalls) {
         continue;
       }
       case OP_LEN: {
-        TValue *rb = RB(i);
-        switch (ttype(rb)) {
-          case LUA_TTABLE: {
-            setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
-            break;
-          }
-          case LUA_TSTRING: {
-            setnvalue(ra, cast_num(tsvalue(rb)->len));
-            break;
-          }
-          default: {  /* try metamethod */
-            Protect(
-              if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
-                luaG_typeerror(L, rb, "get length of");
-            )
-          }
-        }
+        Protect(objlen(L, ra, RB(i)));
         continue;
       }
       case OP_CONCAT: {
-- 
GitLab