From: Josef 'Jeff' Sipek Date: Thu, 4 Mar 2021 20:35:37 +0000 (-0500) Subject: lib-lua: Implement a Lua 5.1 compatibility wrapper for lua_tointegerx X-Git-Tag: 2.3.15~296 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=cdf6260258ceebaddb1412cc6f773515e15ba640;p=thirdparty%2Fdovecot%2Fcore.git lib-lua: Implement a Lua 5.1 compatibility wrapper for lua_tointegerx --- diff --git a/src/lib-lua/Makefile.am b/src/lib-lua/Makefile.am index fd18ce1e37..491fd0f167 100644 --- a/src/lib-lua/Makefile.am +++ b/src/lib-lua/Makefile.am @@ -14,6 +14,7 @@ libdovecot_lua_la_LIBADD = ../lib-dovecot/libdovecot.la $(LUA_LIBS) libdovecot_lua_la_LDFLAGS = -export-dynamic headers = \ + dlua-compat.h \ dlua-script.h \ dlua-script-private.h diff --git a/src/lib-lua/dlua-compat.c b/src/lib-lua/dlua-compat.c index fbc55659de..f057b09c31 100644 --- a/src/lib-lua/dlua-compat.c +++ b/src/lib-lua/dlua-compat.c @@ -1,6 +1,7 @@ /* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "strnum.h" #include "dlua-script-private.h" #if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 @@ -23,3 +24,70 @@ void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { lua_pop(L, nup); } #endif + +#ifndef HAVE_LUA_TOINTEGERX +# if LUA_VERSION_NUM >= 502 +# error "Lua 5.2+ should have lua_tointegerx()" +# endif +/* + * Lua 5.2 added lua_tointegerx() which tells us whether or not the + * input was an integer. In Lua 5.1, we have to emulate it to the best of + * our ability. + */ +lua_Integer lua_tointegerx(lua_State *L, int idx, int *isnum_r) +{ + lua_Integer integer; + lua_Number number; + unsigned long ulong; + const char *str; + + switch (lua_type(L, idx)) { + case LUA_TSTRING: + /* convert using str_to_long() */ + str = lua_tostring(L, idx); + + if (str_to_long(str, &integer) == 0) { + *isnum_r = 1; + return integer; + } + + /* skip over leading 0x */ + if (strncasecmp(str, "0x", 2) != 0) + break; /* no leading 0x ==> not a hex number */ + + str += 2; + + if (str_to_ulong_hex(str, &ulong) == 0) { + bool ok; + + if (sizeof(lua_Integer) == sizeof(int32_t)) + ok = ulong <= INT32_MAX; + else if (sizeof(lua_Integer) == sizeof(int64_t)) + ok = ulong <= INT64_MAX; + else + i_panic("Don't know how to convert from lua_Integer to C99 type"); + + *isnum_r = ok ? 1 : 0; + return ulong; + } + + break; + case LUA_TNUMBER: + /* use lua helper macro */ + number = lua_tonumber(L, idx); + + /* Lua 5.1-only macro from luaconf.h */ + lua_number2integer(integer, number); + + *isnum_r = (((lua_Number) integer) == number) ? 1 : 0; + + return integer; + default: + break; + } + + /* not an integer */ + *isnum_r = 0; + return 0; +} +#endif diff --git a/src/lib-lua/dlua-compat.h b/src/lib-lua/dlua-compat.h new file mode 100644 index 0000000000..d35f474597 --- /dev/null +++ b/src/lib-lua/dlua-compat.h @@ -0,0 +1,16 @@ +#ifndef DLUA_COMPAT_H +#define DLUA_COMPAT_H + +/* + * In general, make whatever Lua version we have behave more like Lua 5.3. + */ + +#ifndef HAVE_LUA_TOINTEGERX +/* + * Lua 5.2 and 5.3 both have lua_tointegerx(), but their behavior is subtly + * different. Our compatibility wrapper matches the 5.3 behavior. + */ +lua_Integer lua_tointegerx(lua_State *L, int idx, int *isnum_r); +#endif + +#endif diff --git a/src/lib-lua/dlua-script-private.h b/src/lib-lua/dlua-script-private.h index c4d4fa8e37..551d806060 100644 --- a/src/lib-lua/dlua-script-private.h +++ b/src/lib-lua/dlua-script-private.h @@ -4,6 +4,7 @@ #include "dlua-script.h" #include "lualib.h" #include "lauxlib.h" +#include "dlua-compat.h" #if !defined(LUA_VERSION_NUM) #define lua_setfield(L, i, k) (lua_pushstring(L, k), lua_settable(L, i)) diff --git a/src/lib-lua/test-lua.c b/src/lib-lua/test-lua.c index f576a586fb..607f275d93 100644 --- a/src/lib-lua/test-lua.c +++ b/src/lib-lua/test-lua.c @@ -3,6 +3,8 @@ #include "test-lib.h" #include "dlua-script-private.h" +#include + static int dlua_test_assert(lua_State *L) { struct dlua_script *script = dlua_script_from_state(L); @@ -62,9 +64,128 @@ static void test_lua(void) test_end(); } +/* check lua_tointegerx against top-of-stack item */ +static void check_tointegerx_compat(lua_State *L, int expected_isnum, + lua_Integer expected_value) +{ + lua_Integer value; + int isnum; + + value = lua_tointegerx(L, -1, &isnum); + test_assert(isnum == expected_isnum); + + if (isnum == 1) + test_assert(value == expected_value); + + lua_pop(L, 1); +} + +static void test_compat_tointegerx(void) +{ + static const struct { + const char *input; + lua_Integer output; + int isnum; + } str_tests[] = { + { "-1", -1, 1 }, + { "0", 0, 1 }, + { "1", 1, 1 }, + { "-2147483648", -2147483648, 1 }, + { "2147483647", 2147483647, 1 }, + { "0x123", 0x123, 1 }, + { "0123", 123, 1 }, /* NB: lua doesn't use leading zero for octal */ + { "0xabcdef", 0xabcdef, 1 }, + { "0xabcdefg", 0, 0 }, + { "abc", 0, 0 }, + + /* + * The following tests fail with Lua 5.2, but work on 5.1 & + * 5.3. (See lua_tointegerx() comment in dlua-compat.h.) + * + * We just hack around it and provide a different set of + * expected test results for 5.2. + */ +#if LUA_VERSION_NUM != 502 + { "1.525", 0, 0 }, + { "52.51", 0, 0 }, +#else + { "52.51", 52, 1 }, +#endif + }; + static const struct { + lua_Number input; + lua_Integer output; + int isnum; + } num_tests[] = { + { -1, -1, 1 }, + { 0, 0, 1 }, + { 1, 1, 1 }, + { INT_MIN, INT_MIN, 1 }, + { INT_MAX, INT_MAX, 1 }, + + /* + * The following tests fail with Lua 5.2, but work on 5.1 & + * 5.3. (See lua_tointegerx() comment in dlua-compat.h.) + * + * We just hack around it and provide a different set of + * expected test results for 5.2. + */ +#if LUA_VERSION_NUM != 502 + { 1.525, 0, 0 }, + { 52.51, 0, 0 }, + { NAN, 0, 0 }, + { +INFINITY, 0, 0}, + { -INFINITY, 0, 0}, +#else + { 1.525, 1, 1 }, + { 52.51, 52, 1 }, +#endif + }; + static const struct { + lua_Integer input; + lua_Integer output; + } int_tests[] = { + { -1, -1 }, + { 0, 0 }, + { 1, 1 }, + { INT_MIN, INT_MIN }, + { INT_MAX, INT_MAX }, + }; + struct dlua_script *script; + const char *error; + size_t i; + + test_begin("lua compat tostringx"); + + test_assert(dlua_script_create_string("", &script, NULL, &error) == 0); + + for (i = 0; i < N_ELEMENTS(str_tests); i++) { + lua_pushstring(script->L, str_tests[i].input); + check_tointegerx_compat(script->L, str_tests[i].isnum, + str_tests[i].output); + } + + for (i = 0; i < N_ELEMENTS(num_tests); i++) { + lua_pushnumber(script->L, num_tests[i].input); + check_tointegerx_compat(script->L, num_tests[i].isnum, + num_tests[i].output); + } + + for (i = 0; i < N_ELEMENTS(int_tests); i++) { + lua_pushinteger(script->L, int_tests[i].input); + check_tointegerx_compat(script->L, 1, + int_tests[i].output); + } + + dlua_script_unref(&script); + + test_end(); +} + int main(void) { void (*tests[])(void) = { test_lua, + test_compat_tointegerx, NULL };