From 26679ea35bf261f2b5edc2fb2f5cc4df483fd50d Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Wed, 8 Nov 2017 12:50:23 -0200 Subject: new function 'luaV_flttointeger' to convert floats to integers (without string coercions) + string operands to bitwise operations handled by string metamethods --- lcode.c | 6 ++--- ldebug.c | 4 ++-- lobject.c | 4 ++-- lstrlib.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- ltable.c | 6 ++--- ltm.c | 5 ++-- lvm.c | 60 +++++++++++++++++++++++++++-------------------- lvm.h | 9 ++++++- 8 files changed, 126 insertions(+), 48 deletions(-) diff --git a/lcode.c b/lcode.c index 5ca597eb..c36a49ad 100644 --- a/lcode.c +++ b/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); diff --git a/ldebug.c b/ldebug.c index e1f9c015..037809a2 100644 --- a/ldebug.c +++ b/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)); } diff --git a/lobject.c b/lobject.c index 470d75db..32cdc4b0 100644 --- a/lobject.c +++ b/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; } diff --git a/lstrlib.c b/lstrlib.c index c7cfa421..626c9159 100644 --- a/lstrlib.c +++ b/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} }; diff --git a/ltable.c b/ltable.c index 37fc3d0b..33c6852a 100644 --- a/ltable.c +++ b/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 */ diff --git a/ltm.c b/ltm.c index 91f622e4..4c03d2e4 100644 --- a/ltm.c +++ b/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"); diff --git a/lvm.c b/lvm.c index 009dfc57..24208cdd 100644 --- a/lvm.c +++ b/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 { diff --git a/lvm.h b/lvm.h index 6bb5818d..c2e25574 100644 --- a/lvm.h +++ b/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, -- cgit v1.2.3-55-g6feb