瀏覽代碼

new function 'luaV_flttointeger' to convert floats to integers (without
string coercions) + string operands to bitwise operations handled
by string metamethods

Roberto Ierusalimschy 7 年之前
父節點
當前提交
26679ea35b
共有 8 個文件被更改,包括 126 次插入48 次删除
  1. 3 3
      lcode.c
  2. 2 2
      ldebug.c
  3. 2 2
      lobject.c
  4. 71 9
      lstrlib.c
  5. 3 3
      ltable.c
  6. 2 3
      ltm.c
  7. 35 25
      lvm.c
  8. 8 1
      lvm.h

+ 3 - 3
lcode.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lcode.c,v 2.130 2017/10/04 21:56:32 roberto Exp roberto $
+** $Id: lcode.c,v 2.131 2017/11/07 17:20:42 roberto Exp roberto $
 ** Code generator for Lua
 ** See Copyright Notice in lua.h
 */
@@ -611,7 +611,7 @@ static void luaK_float (FuncState *fs, int reg, lua_Number f) {
   TValue v;
   lua_Integer fi;
   setfltvalue(&v, f);
-  if (luaV_tointeger(&v, &fi, 0) &&
+  if (luaV_flttointeger(&v, &fi, 0) &&
       l_castS2U(fi) + MAXARG_sBx <= l_castS2U(MAXARG_Bx))
     luaK_codeAsBx(fs, OP_LOADF, reg, cast_int(fi));
   else
@@ -1146,7 +1146,7 @@ static int validop (int op, TValue *v1, TValue *v2) {
     case LUA_OPBAND: case LUA_OPBOR: case LUA_OPBXOR:
     case LUA_OPSHL: case LUA_OPSHR: case LUA_OPBNOT: {  /* conversion errors */
       lua_Integer i;
-      return (tointeger(v1, &i) && tointeger(v2, &i));
+      return (tointegerns(v1, &i) && tointegerns(v2, &i));
     }
     case LUA_OPDIV: case LUA_OPIDIV: case LUA_OPMOD:  /* division by 0 */
       return (nvalue(v2) != 0);

+ 2 - 2
ldebug.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ldebug.c,v 2.140 2017/11/07 13:25:26 roberto Exp roberto $
+** $Id: ldebug.c,v 2.141 2017/11/07 17:20:42 roberto Exp roberto $
 ** Debug Interface
 ** See Copyright Notice in lua.h
 */
@@ -670,7 +670,7 @@ l_noret luaG_opinterror (lua_State *L, const TValue *p1,
 */
 l_noret luaG_tointerror (lua_State *L, const TValue *p1, const TValue *p2) {
   lua_Integer temp;
-  if (!tointeger(p1, &temp))
+  if (!tointegerns(p1, &temp))
     p2 = p1;
   luaG_runerror(L, "number%s has no integer representation", varinfo(L, p2));
 }

+ 2 - 2
lobject.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lobject.c,v 2.117 2017/07/07 16:34:32 roberto Exp roberto $
+** $Id: lobject.c,v 2.118 2017/10/10 20:05:40 roberto Exp roberto $
 ** Some generic functions over Lua objects
 ** See Copyright Notice in lua.h
 */
@@ -127,7 +127,7 @@ int luaO_rawarith (lua_State *L, int op, const TValue *p1, const TValue *p2,
     case LUA_OPSHL: case LUA_OPSHR:
     case LUA_OPBNOT: {  /* operate only on integers */
       lua_Integer i1; lua_Integer i2;
-      if (tointeger(p1, &i1) && tointeger(p2, &i2)) {
+      if (tointegerns(p1, &i1) && tointegerns(p2, &i2)) {
         setivalue(res, intarith(L, op, i1, i2));
         return 1;
       }

+ 71 - 9
lstrlib.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstrlib.c,v 1.256 2017/05/19 16:29:40 roberto Exp roberto $
+** $Id: lstrlib.c,v 1.257 2017/07/07 16:34:32 roberto Exp roberto $
 ** Standard library for string operations and pattern-matching
 ** See Copyright Notice in lua.h
 */
@@ -220,17 +220,49 @@ static int tonum (lua_State *L, int arg) {
 }
 
 
+static int toint (lua_State *L, int arg) {
+  if (!tonum(L, arg))
+    return 0;  /* not coercible to a number */
+  else if (lua_isinteger(L, arg))
+    return 1;  /* already an integer */
+  else {  /* a float */
+    int ok;
+    lua_Integer n = lua_tointegerx(L, arg, &ok);
+    if (!ok)
+      return 0;
+    else {
+      lua_pop(L, 1);  /* remove the float */
+      lua_pushinteger(L, n);  /* push an integer */
+      return 1;
+    }
+  }
+}
+
+
+static void trymt (lua_State *L, const char *mtname) {
+  lua_settop(L, 2);  /* back to the original arguments */
+  if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname))
+    luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
+                  luaL_typename(L, -2), luaL_typename(L, -1));
+  lua_insert(L, -3);  /* put metamethod before arguments */
+  lua_call(L, 2, 1);  /* call metamethod */
+}
+
+
 static int arith (lua_State *L, int op, const char *mtname) {
   if (tonum(L, 1) && tonum(L, 2))
     lua_arith(L, op);  /* result will be on the top */
-  else {
-    lua_settop(L, 2);  /* back to the original arguments */
-    if (lua_type(L, 2) == LUA_TSTRING || !luaL_getmetafield(L, 2, mtname))
-      return luaL_error(L, "attempt to %s a '%s' with a '%s'", mtname + 2,
-                           luaL_typename(L, -2), luaL_typename(L, -1));
-    lua_insert(L, -3);  /* put metamethod before arguments */
-    lua_call(L, 2, 1);  /* call metamethod */
-  }
+  else
+    trymt(L, mtname);
+  return 1;
+}
+
+
+static int bitwise (lua_State *L, int op, const char *mtname) {
+  if (toint(L, 1) && toint(L, 2))
+    lua_arith(L, op);  /* result will be on the top */
+  else
+    trymt(L, mtname);
   return 1;
 }
 
@@ -267,6 +299,30 @@ static int arith_unm (lua_State *L) {
   return arith(L, LUA_OPUNM, "__unm");
 }
 
+static int bitwise_band (lua_State *L) {
+  return bitwise(L, LUA_OPBAND, "__band");
+}
+
+static int bitwise_bor (lua_State *L) {
+  return bitwise(L, LUA_OPBOR, "__bor");
+}
+
+static int bitwise_bxor (lua_State *L) {
+  return bitwise(L, LUA_OPBXOR, "__bxor");
+}
+
+static int bitwise_shl (lua_State *L) {
+  return bitwise(L, LUA_OPSHL, "__shl");
+}
+
+static int bitwise_shr (lua_State *L) {
+  return bitwise(L, LUA_OPSHR, "__shr");
+}
+
+static int bitwise_bnot (lua_State *L) {
+  return bitwise(L, LUA_OPBNOT, "__bnot");
+}
+
 
 static const luaL_Reg stringmetamethods[] = {
   {"__add", arith_add},
@@ -277,6 +333,12 @@ static const luaL_Reg stringmetamethods[] = {
   {"__div", arith_div},
   {"__idiv", arith_idiv},
   {"__unm", arith_unm},
+  {"__band", bitwise_band},
+  {"__bor", bitwise_bor},
+  {"__bxor", bitwise_bxor},
+  {"__shl", bitwise_shl},
+  {"__shr", bitwise_shr},
+  {"__bnot", bitwise_bnot},
   {"__index", NULL},  /* placeholder */
   {NULL, NULL}
 };

+ 3 - 3
ltable.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltable.c,v 2.124 2017/06/12 14:21:44 roberto Exp roberto $
+** $Id: ltable.c,v 2.125 2017/06/29 15:06:44 roberto Exp roberto $
 ** Lua tables (hash)
 ** See Copyright Notice in lua.h
 */
@@ -495,7 +495,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
   if (ttisnil(key)) luaG_runerror(L, "table index is nil");
   else if (ttisfloat(key)) {
     lua_Integer k;
-    if (luaV_tointeger(key, &k, 0)) {  /* does index fit in an integer? */
+    if (luaV_flttointeger(key, &k, 0)) {  /* does index fit in an integer? */
       setivalue(&aux, k);
       key = &aux;  /* insert it as an integer */
     }
@@ -604,7 +604,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
     case LUA_TNIL: return luaO_nilobject;
     case LUA_TNUMFLT: {
       lua_Integer k;
-      if (luaV_tointeger(key, &k, 0)) /* index is int? */
+      if (luaV_flttointeger(key, &k, 0)) /* index is an integral? */
         return luaH_getint(t, k);  /* use specialized version */
       /* else... */
     }  /* FALLTHROUGH */

+ 2 - 3
ltm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltm.c,v 2.45 2017/10/04 15:49:05 roberto Exp $
+** $Id: ltm.c,v 2.47 2017/11/07 13:25:26 roberto Exp roberto $
 ** Tag methods
 ** See Copyright Notice in lua.h
 */
@@ -153,8 +153,7 @@ void luaT_trybinTM (lua_State *L, const TValue *p1, const TValue *p2,
       /* call never returns, but to avoid warnings: *//* FALLTHROUGH */
       case TM_BAND: case TM_BOR: case TM_BXOR:
       case TM_SHL: case TM_SHR: case TM_BNOT: {
-        lua_Number dummy;
-        if (tonumber(p1, &dummy) && tonumber(p2, &dummy))
+        if (ttisnumber(p1) && ttisnumber(p2))
           luaG_tointerror(L, p1, p2);
         else
           luaG_opinterror(L, p1, p2, "perform bitwise operation on");

+ 35 - 25
lvm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lvm.c,v 2.306 2017/11/07 13:25:26 roberto Exp roberto $
+** $Id: lvm.c,v 2.307 2017/11/07 17:20:42 roberto Exp roberto $
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -74,7 +74,7 @@ int luaV_tonumber_ (const TValue *obj, lua_Number *n) {
     *n = cast_num(ivalue(obj));
     return 1;
   }
-  else if (cvt2num(obj) &&  /* string convertible to number? */
+  else if (cvt2num(obj) &&  /* string coercible to number? */
             luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) {
     *n = nvalue(&v);  /* convert result of 'luaO_str2num' to a float */
     return 1;
@@ -85,15 +85,15 @@ int luaV_tonumber_ (const TValue *obj, lua_Number *n) {
 
 
 /*
-** try to convert a value to an integer, rounding according to 'mode':
+** try to convert a float to an integer, rounding according to 'mode':
 ** mode == 0: accepts only integral values
 ** mode == 1: takes the floor of the number
 ** mode == 2: takes the ceil of the number
 */
-int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
-  TValue v;
- again:
-  if (ttisfloat(obj)) {
+int luaV_flttointeger (const TValue *obj, lua_Integer *p, int mode) {
+  if (!ttisfloat(obj))
+    return 0;
+  else {
     lua_Number n = fltvalue(obj);
     lua_Number f = l_floor(n);
     if (n != f) {  /* not an integral value? */
@@ -103,16 +103,23 @@ int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
     }
     return lua_numbertointeger(f, p);
   }
-  else if (ttisinteger(obj)) {
+}
+
+
+/*
+** try to convert a value to an integer. ("Fast track" is handled
+** by macro 'tointeger'.)
+*/
+int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
+  TValue v;
+  if (cvt2num(obj) && luaO_str2num(svalue(obj), &v) == vslen(obj) + 1)
+    obj = &v;  /* change string to its corresponding number */
+  if (ttisinteger(obj)) {
     *p = ivalue(obj);
     return 1;
   }
-  else if (cvt2num(obj) &&
-            luaO_str2num(svalue(obj), &v) == vslen(obj) + 1) {
-    obj = &v;
-    goto again;  /* convert result from 'luaO_str2num' to an integer */
-  }
-  return 0;  /* conversion failed */
+  else
+    return luaV_flttointeger(obj, p, mode);
 }
 
 
@@ -120,9 +127,9 @@ int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
 ** Try to convert a 'for' limit to an integer, preserving the semantics
 ** of the loop.  (The following explanation assumes a non-negative step;
 ** it is valid for negative steps mutatis mutandis.)
-** If the limit can be converted to an integer, rounding down, that is
-** it.
-** Otherwise, check whether the limit can be converted to a number.  If
+** If the limit is an integer or can be converted to an integer,
+** rounding down, that is it.
+** Otherwise, check whether the limit can be converted to a float.  If
 ** the number is too large, it is OK to set the limit as LUA_MAXINTEGER,
 ** which means no limit.  If the number is too negative, the loop
 ** should not run, because any initial integer value is larger than the
@@ -133,7 +140,10 @@ int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) {
 static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step,
                      int *stopnow) {
   *stopnow = 0;  /* usually, let loops run */
-  if (!luaV_tointeger(obj, p, (step < 0 ? 2 : 1))) {  /* not fit in integer? */
+  if (ttisinteger(obj))
+    *p = ivalue(obj);
+  else if (!luaV_tointeger(obj, p, (step < 0 ? 2 : 1))) {
+    /* not coercible to in integer */
     lua_Number n;  /* try to convert to float */
     if (!tonumber(obj, &n)) /* cannot convert to float? */
       return 0;  /* not a number */
@@ -411,7 +421,7 @@ int luaV_equalobj (lua_State *L, const TValue *t1, const TValue *t2) {
       return 0;  /* only numbers can be equal with different variants */
     else {  /* two numbers with different variants */
       lua_Integer i1, i2;  /* compare them as integers */
-      return (tointeger(t1, &i1) && tointeger(t2, &i2) && i1 == i2);
+      return (tointegerns(t1, &i1) && tointegerns(t2, &i2) && i1 == i2);
     }
   }
   /* values have same type and same variant */
@@ -1144,7 +1154,7 @@ void luaV_execute (lua_State *L) {
         TValue *rb = vRB(i);
         TValue *rc = vRC(i);
         lua_Integer ib; lua_Integer ic;
-        if (tointeger(rb, &ib) && tointeger(rc, &ic)) {
+        if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
           setivalue(s2v(ra), intop(&, ib, ic));
         }
         else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BAND)); }
@@ -1154,7 +1164,7 @@ void luaV_execute (lua_State *L) {
         TValue *rb = vRB(i);
         TValue *rc = vRC(i);
         lua_Integer ib; lua_Integer ic;
-        if (tointeger(rb, &ib) && tointeger(rc, &ic)) {
+        if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
           setivalue(s2v(ra), intop(|, ib, ic));
         }
         else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BOR)); }
@@ -1164,7 +1174,7 @@ void luaV_execute (lua_State *L) {
         TValue *rb = vRB(i);
         TValue *rc = vRC(i);
         lua_Integer ib; lua_Integer ic;
-        if (tointeger(rb, &ib) && tointeger(rc, &ic)) {
+        if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
           setivalue(s2v(ra), intop(^, ib, ic));
         }
         else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_BXOR)); }
@@ -1174,7 +1184,7 @@ void luaV_execute (lua_State *L) {
         TValue *rb = vRB(i);
         TValue *rc = vRC(i);
         lua_Integer ib; lua_Integer ic;
-        if (tointeger(rb, &ib) && tointeger(rc, &ic)) {
+        if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
           setivalue(s2v(ra), luaV_shiftl(ib, ic));
         }
         else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHL)); }
@@ -1184,7 +1194,7 @@ void luaV_execute (lua_State *L) {
         TValue *rb = vRB(i);
         TValue *rc = vRC(i);
         lua_Integer ib; lua_Integer ic;
-        if (tointeger(rb, &ib) && tointeger(rc, &ic)) {
+        if (tointegerns(rb, &ib) && tointegerns(rc, &ic)) {
           setivalue(s2v(ra), luaV_shiftl(ib, -ic));
         }
         else { Protect(luaT_trybinTM(L, rb, rc, ra, TM_SHR)); }
@@ -1248,7 +1258,7 @@ void luaV_execute (lua_State *L) {
       vmcase(OP_BNOT) {
         TValue *rb = vRB(i);
         lua_Integer ib;
-        if (tointeger(rb, &ib)) {
+        if (tointegerns(rb, &ib)) {
           setivalue(s2v(ra), intop(^, ~l_castS2U(0), ib));
         }
         else {

+ 8 - 1
lvm.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lvm.h,v 2.45 2017/06/29 15:06:44 roberto Exp roberto $
+** $Id: lvm.h,v 2.46 2017/07/07 16:34:32 roberto Exp roberto $
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -53,6 +53,12 @@
     (ttisinteger(o) ? (*(i) = ivalue(o), 1) : luaV_tointeger(o,i,LUA_FLOORN2I))
 
 
+/* convert an object to an integer (without string coercion) */
+#define tointegerns(o,i) \
+    (ttisinteger(o) ? (*(i) = ivalue(o), 1) \
+                    : luaV_flttointeger(o,i,LUA_FLOORN2I))
+
+
 #define intop(op,v1,v2) l_castU2S(l_castS2U(v1) op l_castS2U(v2))
 
 #define luaV_rawequalobj(t1,t2)		luaV_equalobj(NULL,t1,t2)
@@ -100,6 +106,7 @@ LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r);
 LUAI_FUNC int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r);
 LUAI_FUNC int luaV_tonumber_ (const TValue *obj, lua_Number *n);
 LUAI_FUNC int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode);
+LUAI_FUNC int luaV_flttointeger (const TValue *obj, lua_Integer *p, int mode);
 LUAI_FUNC void luaV_finishget (lua_State *L, const TValue *t, TValue *key,
                                StkId val, const TValue *slot);
 LUAI_FUNC void luaV_finishset (lua_State *L, const TValue *t, TValue *key,