]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-lua: Add table get helpers
authorJosef 'Jeff' Sipek <jeff.sipek@open-xchange.com>
Wed, 3 Mar 2021 22:08:07 +0000 (17:08 -0500)
committerjeff.sipek <jeff.sipek@open-xchange.com>
Wed, 10 Mar 2021 19:12:41 +0000 (19:12 +0000)
src/lib-lua/Makefile.am
src/lib-lua/dlua-compat.c
src/lib-lua/dlua-script-private.h
src/lib-lua/dlua-table.c [new file with mode: 0644]
src/lib-lua/test-lua.c

index 491fd0f16700c7369bbdd3eb0981930278e08d0d..a76e9b8e90d1013d4c393b3fac12811d14aa82b8 100644 (file)
@@ -7,7 +7,8 @@ pkglib_LTLIBRARIES = libdovecot-lua.la
 libdovecot_lua_la_SOURCES = \
        dlua-script.c \
        dlua-dovecot.c \
-       dlua-compat.c
+       dlua-compat.c \
+       dlua-table.c
 # Note: the only things this lib should depend on are libdovecot and lua.
 libdovecot_lua_la_DEPENDENCIES = ../lib-dovecot/libdovecot.la
 libdovecot_lua_la_LIBADD = ../lib-dovecot/libdovecot.la $(LUA_LIBS)
index 39d5db7cc8eb5bb49a5e883765b4beb3c7a381d0..f1b55469102187651a7a2ac75e931e1fab24871e 100644 (file)
@@ -63,37 +63,45 @@ lua_Integer lua_tointegerx(lua_State *L, int idx, int *isnum_r)
 {
        lua_Integer integer;
        lua_Number number;
-       unsigned long ulong;
        const char *str;
 
+       /*
+        * Unfortunately, Lua 5.1 doesn't provide MIN/MAX value macros for
+        * the lua_Integer type, so we hardcode the assumption that it is
+        * the same size as ptrdiff_t.  This matches what Lua does by
+        * default.
+        *
+        * If this compile-time assertion fails, don't forget to change the
+        * PTRDIFF_{MIN,MAX} usage below as well.
+        */
+       (void) COMPILE_ERROR_IF_TRUE(sizeof(lua_Integer) != sizeof(ptrdiff_t));
+
        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;
-               }
+               if (strncasecmp(str, "0x", 2) == 0) {
+                       /* hex */
+                       uintmax_t tmp;
 
-               /* skip over leading 0x */
-               if (strncasecmp(str, "0x", 2) != 0)
-                       break; /* no leading 0x ==> not a hex number */
+                       /* skip over leading 0x */
+                       str += 2;
 
-               str += 2;
+                       if (str_to_uintmax_hex(str, &tmp) < 0)
+                               break;
 
-               if (str_to_ulong_hex(str, &ulong) == 0) {
-                       bool ok;
+                       *isnum_r = (tmp <= PTRDIFF_MAX) ? 1 : 0;
+                       return tmp;
+               } else {
+                       /* try decimal */
+                       intmax_t tmp;
 
-                       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");
+                       if (str_to_intmax(str, &tmp) < 0)
+                               break;
 
-                       *isnum_r = ok ? 1 : 0;
-                       return ulong;
+                       *isnum_r = ((tmp >= PTRDIFF_MIN) && (tmp <= PTRDIFF_MAX)) ? 1 : 0;
+                       return tmp;
                }
 
                break;
index edbfb6b8cf2d807778e5e7b2cf65e6bafc5d0f0c..5e1809ac2532abba0bc3e2d661f7af351ec49910 100644 (file)
@@ -93,6 +93,79 @@ void dlua_push_event(lua_State *L, struct event *event);
 /* get event from given stack position */
 struct event *dlua_check_event(lua_State *L, int arg);
 
+/*
+ * Returns field from a Lua table
+ *
+ * There are different variants of these that allow for different key types
+ * and different value types.  In general, the function name scheme is:
+ *
+ *     dlua_table_get_<return type>_by_<key type>
+ *
+ * The _by_{str,int} variants use the supplied field value as the table key.
+ *
+ * The _by_thread variants use the current thread's thread object as the
+ * table key.
+ *
+ * Returns:
+ *   -1 = incompatible value type
+ *    0 = nil or not found
+ *    1 = value found
+ */
+int dlua_table_get_luainteger_by_str(lua_State *L, int idx, const char *field, lua_Integer *value_r);
+int dlua_table_get_int_by_str(lua_State *L, int idx, const char *field, int *value_r);
+int dlua_table_get_intmax_by_str(lua_State *L, int idx, const char *field, intmax_t *value_r);
+int dlua_table_get_uint_by_str(lua_State *L, int idx, const char *field, unsigned int *value_r);
+int dlua_table_get_uintmax_by_str(lua_State *L, int idx, const char *field, uintmax_t *value_r);
+int dlua_table_get_number_by_str(lua_State *L, int idx, const char *field, lua_Number *value_r);
+int dlua_table_get_bool_by_str(lua_State *L, int idx, const char *field, bool *value_r);
+int dlua_table_get_string_by_str(lua_State *L, int idx, const char *field, const char **value_r);
+int dlua_table_get_data_by_str(lua_State *L, int idx, const char *field, const unsigned char **value_r, size_t *len_r);
+
+int dlua_table_get_luainteger_by_int(lua_State *L, int idx, lua_Integer field, lua_Integer *value_r);
+int dlua_table_get_int_by_int(lua_State *L, int idx, lua_Integer field, int *value_r);
+int dlua_table_get_intmax_by_int(lua_State *L, int idx, lua_Integer field, intmax_t *value_r);
+int dlua_table_get_uint_by_int(lua_State *L, int idx, lua_Integer field, unsigned int *value_r);
+int dlua_table_get_uintmax_by_int(lua_State *L, int idx, lua_Integer field, uintmax_t *value_r);
+int dlua_table_get_number_by_int(lua_State *L, int idx, lua_Integer field, lua_Number *value_r);
+int dlua_table_get_bool_by_int(lua_State *L, int idx, lua_Integer field, bool *value_r);
+int dlua_table_get_string_by_int(lua_State *L, int idx, lua_Integer field, const char **value_r);
+int dlua_table_get_data_by_int(lua_State *L, int idx, lua_Integer field, const unsigned char **value_r, size_t *len_r);
+
+int dlua_table_get_luainteger_by_thread(lua_State *L, int idx, lua_Integer *value_r);
+int dlua_table_get_int_by_thread(lua_State *L, int idx, int *value_r);
+int dlua_table_get_intmax_by_thread(lua_State *L, int idx, intmax_t *value_r);
+int dlua_table_get_uint_by_thread(lua_State *L, int idx, unsigned int *value_r);
+int dlua_table_get_uintmax_by_thread(lua_State *L, int idx, uintmax_t *value_r);
+int dlua_table_get_number_by_thread(lua_State *L, int idx, lua_Number *value_r);
+int dlua_table_get_bool_by_thread(lua_State *L, int idx, bool *value_r);
+int dlua_table_get_string_by_thread(lua_State *L, int idx, const char **value_r);
+int dlua_table_get_data_by_thread(lua_State *L, int idx, const unsigned char **value_r, size_t *len_r);
+
+/*
+ * Pushes onto the stack the value t[k], where t is the value at the given
+ * index and k is field argument.  Unlike lua_gettable(), this function
+ * checks the type of the retrieved value against the passed in type.
+ * [-1,+0..1,e]
+ *
+ * There are different variants of these that allow for different key types.
+ * In general, the function name scheme is:
+ *
+ *     dlua_table_get_by_<key type>
+ *
+ * The _by_{str,int} variants use the supplied field value as the table key.
+ *
+ * The _by_thread variants use the current thread's thread object as the
+ * table key.
+ *
+ * Returns:
+ *   -1 = incompatible value type (nothing is pushed)
+ *    0 = nil or not found (nothing is pushed)
+ *    1 = value found (retrieved value is pushed to the top of the stack)
+ */
+int dlua_table_get_by_str(lua_State *L, int idx, int type, const char *field);
+int dlua_table_get_by_int(lua_State *L, int idx, int type, lua_Integer field);
+int dlua_table_get_by_thread(lua_State *L, int idx, int type);
+
 /* dumps current stack as i_debug lines */
 void dlua_dump_stack(lua_State *L);
 
diff --git a/src/lib-lua/dlua-table.c b/src/lib-lua/dlua-table.c
new file mode 100644 (file)
index 0000000..8467a7e
--- /dev/null
@@ -0,0 +1,301 @@
+/* Copyright (c) 2021 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "dlua-script-private.h"
+
+/*
+ * Adjust the index by the specified delta.
+ *
+ * In a couple of places we need to adjust the passed in index to reflect
+ * additional items pushed onto the stack.  We cannot blindly adjust the
+ * index because the index could be one of three things and only one of them
+ * is supposed to be ajusted:
+ *
+ *  1. negative number: index relative to top of stack, adjust
+ *  2. positive number: absolute index, don't adjust
+ *  3. special registry index: don't adjust
+ */
+static inline int adj(int idx, int delta)
+{
+       if ((idx == LUA_REGISTRYINDEX) || (idx > 0))
+               return idx;
+       else
+               return idx - delta;
+}
+
+/*
+ * Pushes onto the stack the value t[k], where t is the value at the given
+ * index and k is the value at the top of the stack.  Unlike lua_gettable(),
+ * this function checks the type of the retreived value against the passed
+ * in type. [-1,+0..1,e]
+ *
+ * Return value:
+ *   -1 = incompatible type
+ *    0 = nil or none
+ *    1 = found
+ */
+static int dlua_table_get(lua_State *L, int idx, int type)
+{
+       /* can only work with tables */
+       if (!lua_istable(L, idx))
+               return -1;
+
+       lua_gettable(L, idx);
+
+       /* check if the field was there */
+       if (lua_isnoneornil(L, -1)) {
+               lua_pop(L, 1);
+               return 0;
+       }
+
+       /* check that the field is the expected type */
+       if (lua_type(L, -1) != type) {
+               lua_pop(L, 1);
+               return -1;
+       }
+
+       return 1;
+}
+
+/* Get by string name [-0,+1,e] */
+int dlua_table_get_by_str(lua_State *L, int idx, int type, const char *field)
+{
+       /* push the key */
+       lua_pushstring(L, field);
+
+       return dlua_table_get(L, adj(idx, 1), type);
+}
+
+/* Get by int name [-0,+1,e] */
+int dlua_table_get_by_int(lua_State *L, int idx, int type, lua_Integer field)
+{
+       /* push the key */
+       lua_pushinteger(L, field);
+
+       return dlua_table_get(L, adj(idx, 1), type);
+}
+
+/* Get by thread [-0,+1,e] */
+int dlua_table_get_by_thread(lua_State *L, int idx, int type)
+{
+       /* push the key */
+       lua_pushthread(L);
+
+       return dlua_table_get(L, adj(idx, 1), type);
+}
+
+/* generate a set of functions to access fields of an integral data type */
+#define GET_INTTYPE(fxn, ctype, minval, maxval, unsigned_check)                \
+int fxn##_by_str(lua_State *L, int idx, const char *field,             \
+                ctype *value_r)                                        \
+{                                                                      \
+       lua_Integer tmp;                                                \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_luainteger_by_str(L, idx, field, &tmp);    \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       if (unsigned_check) {                                           \
+               if ((tmp < 0) || (((uintmax_t) tmp) > (maxval)))        \
+                       return -1;                                      \
+        } else {                                                       \
+               if ((tmp < (minval)) || (tmp > (intmax_t) (maxval)))    \
+                       return -1;                                      \
+        }                                                              \
+                                                                       \
+       *value_r = (ctype) tmp;                                         \
+                                                                       \
+       return 1;                                                       \
+}                                                                      \
+int fxn##_by_int(lua_State *L, int idx, lua_Integer field,             \
+                ctype *value_r)                                        \
+{                                                                      \
+       lua_Integer tmp;                                                \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_luainteger_by_int(L, idx, field, &tmp);    \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       if (unsigned_check) {                                           \
+               if ((tmp < 0) || (((uintmax_t) tmp) > (maxval)))        \
+                       return -1;                                      \
+        } else {                                                       \
+               if ((tmp < (minval)) || (tmp > (intmax_t) (maxval)))    \
+                       return -1;                                      \
+        }                                                              \
+                                                                       \
+       *value_r = (ctype) tmp;                                         \
+                                                                       \
+       return 1;                                                       \
+}                                                                      \
+int fxn##_by_thread(lua_State *L, int idx, ctype *value_r)             \
+{                                                                      \
+       lua_Integer tmp;                                                \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_luainteger_by_thread(L, idx, &tmp);        \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       if (unsigned_check) {                                           \
+               if ((tmp < 0) || (((uintmax_t) tmp) > (maxval)))        \
+                       return -1;                                      \
+        } else {                                                       \
+               if ((tmp < (minval)) || (tmp > (intmax_t) (maxval)))    \
+                       return -1;                                      \
+        }                                                              \
+                                                                       \
+       *value_r = (ctype) tmp;                                         \
+                                                                       \
+       return 1;                                                       \
+}
+
+/* generate a set of functions to access fields of a binary data type */
+#define GET_DATAPTR(fxn)                                               \
+int fxn##_by_str(lua_State *L, int idx, const char *field,             \
+                const unsigned char **value_r, size_t *len_r)          \
+{                                                                      \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_by_str(L, idx, LUA_TSTRING, field);        \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       *value_r = (const unsigned char *) lua_tolstring(L, -1, len_r); \
+       lua_pop(L, 1);                                                  \
+                                                                       \
+       return 1;                                                       \
+}                                                                      \
+int fxn##_by_int(lua_State *L, int idx, lua_Integer field,             \
+                const unsigned char **value_r, size_t *len_r)          \
+{                                                                      \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_by_int(L, idx, LUA_TSTRING, field);        \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       *value_r = (const unsigned char *) lua_tolstring(L, -1, len_r); \
+       lua_pop(L, 1);                                                  \
+                                                                       \
+       return 1;                                                       \
+}                                                                      \
+int fxn##_by_thread(lua_State *L, int idx,                             \
+                   const unsigned char **value_r, size_t *len_r)       \
+{                                                                      \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_by_thread(L, idx, LUA_TSTRING);            \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       *value_r = (const unsigned char *) lua_tolstring(L, -1, len_r); \
+       lua_pop(L, 1);                                                  \
+                                                                       \
+       return 1;                                                       \
+}
+
+/* generate a set of functions to access fields of a generic-ish type */
+#define GET_GENERIC(fxn, ctype, ltype, cvt)                            \
+int fxn##_by_str(lua_State *L, int idx, const char *field, ctype *value_r)\
+{                                                                      \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_by_str(L, idx, (ltype), field);            \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       *value_r = cvt(L, -1);                                          \
+       lua_pop(L, 1);                                                  \
+                                                                       \
+       return 1;                                                       \
+}                                                                      \
+int fxn##_by_int(lua_State *L, int idx, lua_Integer field, ctype *value_r)\
+{                                                                      \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_by_int(L, idx, (ltype), field);            \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       *value_r = cvt(L, -1);                                          \
+       lua_pop(L, 1);                                                  \
+                                                                       \
+       return 1;                                                       \
+}                                                                      \
+int fxn##_by_thread(lua_State *L, int idx, ctype *value_r)             \
+{                                                                      \
+       int ret;                                                        \
+                                                                       \
+       ret = dlua_table_get_by_thread(L, idx, (ltype));                \
+       if (ret < 1)                                                    \
+               return ret;                                             \
+                                                                       \
+       *value_r = cvt(L, -1);                                          \
+       lua_pop(L, 1);                                                  \
+                                                                       \
+       return 1;                                                       \
+}
+
+GET_INTTYPE(dlua_table_get_int, int, INT_MIN, INT_MAX, FALSE);
+GET_INTTYPE(dlua_table_get_intmax, intmax_t, INTMAX_MIN, INTMAX_MAX, FALSE);
+GET_INTTYPE(dlua_table_get_uint, unsigned int, 0, UINT_MAX, TRUE);
+GET_INTTYPE(dlua_table_get_uintmax, uintmax_t, 0, UINTMAX_MAX, TRUE);
+
+/* we need to use lua_tointegerx which takes an extra argument */
+int dlua_table_get_luainteger_by_str(lua_State *L, int idx, const char *field,
+                                    lua_Integer *value_r)
+{
+       int isnum;
+       int ret;
+
+       ret = dlua_table_get_by_str(L, idx, LUA_TNUMBER, field);
+       if (ret < 1)
+               return ret;
+
+       *value_r = lua_tointegerx(L, -1, &isnum);
+       lua_pop(L, 1);
+
+       return (isnum == 1) ? 1 : -1;
+}
+
+/* we need to use lua_tointegerx which takes an extra argument */
+int dlua_table_get_luainteger_by_int(lua_State *L, int idx, lua_Integer field,
+                                    lua_Integer *value_r)
+{
+       int isnum;
+       int ret;
+
+       ret = dlua_table_get_by_int(L, idx, LUA_TNUMBER, field);
+       if (ret < 1)
+               return ret;
+
+       *value_r = lua_tointegerx(L, -1, &isnum);
+       lua_pop(L, 1);
+
+       return (isnum == 1) ? 1 : -1;
+}
+
+/* we need to use lua_tointegerx which takes an extra argument */
+int dlua_table_get_luainteger_by_thread(lua_State *L, int idx,
+                                       lua_Integer *value_r)
+{
+       int isnum;
+       int ret;
+
+       ret = dlua_table_get_by_thread(L, idx, LUA_TNUMBER);
+       if (ret < 1)
+               return ret;
+
+       *value_r = lua_tointegerx(L, -1, &isnum);
+       lua_pop(L, 1);
+
+       return (isnum == 1) ? 1 : -1;
+}
+
+GET_GENERIC(dlua_table_get_number, lua_Number, LUA_TNUMBER, lua_tonumber);
+GET_GENERIC(dlua_table_get_bool, bool, LUA_TBOOLEAN, lua_toboolean);
+GET_GENERIC(dlua_table_get_string, const char *, LUA_TSTRING, lua_tostring);
+GET_DATAPTR(dlua_table_get_data);
index 5e7e8e5346eaaf670393b33d14ca9654c9140892..76b7855148e8630fee6c4da22f36eab737e8e3a5 100644 (file)
@@ -20,6 +20,114 @@ static int dlua_test_assert(lua_State *L)
        return 0;
 }
 
+#define GENERATE_GETTERS(name, ctype)                                  \
+static void check_table_get_##name##_ok(struct dlua_script *script,    \
+                                       int idx, ctype expected_value,  \
+                                       const char *str_key,            \
+                                       lua_Integer int_key)            \
+{                                                                      \
+       ctype value;                                                    \
+       int ret;                                                        \
+                                                                       \
+       /* check string key */                                          \
+       ret = dlua_table_get_##name##_by_str(script->L, idx,            \
+                                            str_key, &value);          \
+       test_assert(ret == 1);                                          \
+       test_assert(value == expected_value);                           \
+                                                                       \
+       /* check int key */                                             \
+       ret = dlua_table_get_##name##_by_int(script->L, idx,            \
+                                            int_key, &value);          \
+       test_assert(ret == 1);                                          \
+       test_assert(value == expected_value);                           \
+}                                                                      \
+static void check_table_get_##name##_err(struct dlua_script *script,   \
+                                        int idx, int expected_ret,     \
+                                        const char *str_key,           \
+                                        lua_Integer int_key)           \
+{                                                                      \
+       ctype value;                                                    \
+       int ret;                                                        \
+                                                                       \
+       /* check string key */                                          \
+       ret = dlua_table_get_##name##_by_str(script->L, idx,            \
+                                            str_key, &value);          \
+       test_assert(ret == expected_ret);                               \
+                                                                       \
+       /* check int key */                                             \
+       ret = dlua_table_get_##name##_by_int(script->L, idx,            \
+                                            int_key, &value);          \
+       test_assert(ret == expected_ret);                               \
+}
+
+GENERATE_GETTERS(luainteger, lua_Integer);
+GENERATE_GETTERS(int, int);
+GENERATE_GETTERS(intmax, intmax_t);
+GENERATE_GETTERS(uint, unsigned int);
+GENERATE_GETTERS(uintmax, uintmax_t);
+GENERATE_GETTERS(number, lua_Number);
+GENERATE_GETTERS(bool, bool);
+
+/* the string comparison requires us to open-code this */
+static void check_table_get_string_ok(struct dlua_script *script,
+                                     int idx, const char *expected_value,
+                                     const char *str_key,
+                                     lua_Integer int_key)
+{
+       const char *value;
+       int ret;
+
+       /* check string key */
+       ret = dlua_table_get_string_by_str(script->L, idx,
+                                          str_key, &value);
+       test_assert(ret == 1);
+       test_assert_strcmp(value, expected_value);
+
+       /* check int key */
+       ret = dlua_table_get_string_by_int(script->L, idx,
+                                          int_key, &value);
+       test_assert(ret == 1);
+       test_assert_strcmp(value, expected_value);
+
+       /* TODO: check thread key, which is difficult */
+}
+
+/* the string comparison of the _ok function requires us to open-code this */
+static void check_table_get_string_err(struct dlua_script *script,
+                                      int idx, int expected_ret,
+                                      const char *str_key,
+                                      lua_Integer int_key)
+{
+       const char *value;
+       int ret;
+
+       /* check string key */
+       ret = dlua_table_get_string_by_str(script->L, idx,
+                                          str_key, &value);
+       test_assert(ret == expected_ret);
+
+       /* check int key */
+       ret = dlua_table_get_string_by_int(script->L, idx,
+                                          int_key, &value);
+       test_assert(ret == expected_ret);
+
+       /* TODO: check thread key, which is difficult */
+}
+
+static void check_table_missing(struct dlua_script *script, int idx,
+                               const char *str_key,
+                               lua_Integer int_key)
+{
+       check_table_get_luainteger_err(script, idx, 0, str_key, int_key);
+       check_table_get_int_err(script, idx, 0, str_key, int_key);
+       check_table_get_intmax_err(script, idx, 0, str_key, int_key);
+       check_table_get_uint_err(script, idx, 0, str_key, int_key);
+       check_table_get_uintmax_err(script, idx, 0, str_key, int_key);
+       check_table_get_number_err(script, idx, 0, str_key, int_key);
+       check_table_get_bool_err(script, idx, 0, str_key, int_key);
+       check_table_get_string_err(script, idx, 0, str_key, int_key);
+}
+
 static void test_lua(void)
 {
        static const char *luascript =
@@ -41,6 +149,37 @@ static void test_lua(void)
 "  flag = dovecot.clear_flag(flag, 4)\n"
 "  test_assert(\"has_flag(flag, 4) == false\", dovecot.has_flag(flag, 4) == false)\n"
 "  test_assert(\"has_flag(flag, 16) == true\", dovecot.has_flag(flag, 16) == true)\n"
+"end\n"
+"function lua_test_get_table()\n"
+"  t = {}\n"
+"  -- zero\n"
+"  t[\"zero\"] = 0\n"
+"  t[-2] = 0\n"
+"  -- small positive values\n"
+"  t[\"small-positive-int\"] = 1\n"
+"  t[-1] = 1\n"
+"  -- small negative values\n"
+"  t[\"small-negative-int\"] = -5\n"
+"  t[0] = -5\n"
+"  -- large positive float\n"
+"  t[\"large-positive-int\"] = 2^48\n"
+"  t[1] = 2^48\n"
+"  -- large negative float\n"
+"  t[\"large-negative-int\"] = -2^48\n"
+"  t[2] = -2^48\n"
+"  -- small float\n"
+"  t[\"small-float\"] = 1.525\n"
+"  t[3] = 1.525\n"
+"  -- bool: true\n"
+"  t[\"bool-true\"] = true\n"
+"  t[4] = true\n"
+"  -- bool: false\n"
+"  t[\"bool-false\"] = false\n"
+"  t[5] = false\n"
+"  -- string\n"
+"  t[\"str\"] = \"string\"\n"
+"  t[6] = \"string\"\n"
+"  return t\n"
 "end\n";
 
        const char *error = NULL;
@@ -49,6 +188,9 @@ static void test_lua(void)
        test_begin("lua script");
 
        test_assert(dlua_script_create_string(luascript, &script, NULL, &error) == 0);
+       if (error != NULL)
+               i_fatal("dlua_script_init failed: %s", error);
+
        dlua_dovecot_register(script);
 
        dlua_register(script, "test_assert", dlua_test_assert);
@@ -59,6 +201,125 @@ static void test_lua(void)
        lua_getglobal(script->L, "lua_test_flags");
        test_assert(lua_pcall(script->L, 0, 0, 0) == 0);
 
+       lua_getglobal(script->L, "lua_test_get_table");
+       test_assert(lua_pcall(script->L, 0, 1, 0) == 0);
+
+       /*
+        * Check table getters
+        */
+
+       /* lua_Integer */
+       check_table_get_luainteger_ok(script, -1, 0, "zero", -2);
+       check_table_get_luainteger_ok(script, -1, 1, "small-positive-int", -1);
+       check_table_get_luainteger_ok(script, -1, -5, "small-negative-int", 0);
+       check_table_get_luainteger_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+       check_table_get_luainteger_ok(script, -1, -(1ll<<48), "large-negative-int", 2);
+#if LUA_VERSION_NUM != 502
+       check_table_get_luainteger_err(script, -1, -1, "small-float", 3);
+#else
+       check_table_get_uintmax_ok(script, -1, 1, "small-float", 3);
+#endif
+       check_table_get_luainteger_err(script, -1, -1, "bool-true", 4);
+       check_table_get_luainteger_err(script, -1, -1, "bool-false", 5);
+       check_table_get_luainteger_err(script, -1, -1, "str", 6);
+
+       /* int */
+       check_table_get_int_ok(script, -1, 0, "zero", -2);
+       check_table_get_int_ok(script, -1, 1, "small-positive-int", -1);
+       check_table_get_int_ok(script, -1, -5, "small-negative-int", 0);
+       check_table_get_int_err(script, -1, -1, "large-positive-int", 1);
+       check_table_get_int_err(script, -1, -1, "large-negative-int", 2);
+#if LUA_VERSION_NUM != 502
+       check_table_get_int_err(script, -1, -1, "small-float", 3);
+#else
+       check_table_get_uintmax_ok(script, -1, 1, "small-float", 3);
+#endif
+       check_table_get_int_err(script, -1, -1, "bool-true", 4);
+       check_table_get_int_err(script, -1, -1, "bool-false", 5);
+       check_table_get_int_err(script, -1, -1, "str", 6);
+
+       /* intmax_t */
+       check_table_get_intmax_ok(script, -1, 0, "zero", -2);
+       check_table_get_intmax_ok(script, -1, 1, "small-positive-int", -1);
+       check_table_get_intmax_ok(script, -1, -5, "small-negative-int", 0);
+       check_table_get_intmax_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+       check_table_get_intmax_ok(script, -1, -(1ll<<48), "large-negative-int", 2);
+#if LUA_VERSION_NUM != 502
+       check_table_get_intmax_err(script, -1, -1, "small-float", 3);
+#else
+       check_table_get_uintmax_ok(script, -1, 1, "small-float", 3);
+#endif
+       check_table_get_intmax_err(script, -1, -1, "bool-true", 4);
+       check_table_get_intmax_err(script, -1, -1, "bool-false", 5);
+       check_table_get_intmax_err(script, -1, -1, "str", 6);
+
+       /* unsigned int */
+       check_table_get_uint_ok(script, -1, 0, "zero", -2);
+       check_table_get_uint_ok(script, -1, 1, "small-positive-int", -1);
+       check_table_get_uint_err(script, -1, -1, "small-negative-int", 0);
+       check_table_get_uint_err(script, -1, -1, "large-positive-int", 1);
+       check_table_get_uint_err(script, -1, -1, "large-negative-int", 2);
+#if LUA_VERSION_NUM != 502
+       check_table_get_uint_err(script, -1, -1, "small-float", 3);
+#else
+       check_table_get_uintmax_ok(script, -1, 1, "small-float", 3);
+#endif
+       check_table_get_uint_err(script, -1, -1, "bool-true", 4);
+       check_table_get_uint_err(script, -1, -1, "bool-false", 5);
+       check_table_get_uint_err(script, -1, -1, "str", 6);
+
+       /* uintmax_t */
+       check_table_get_uintmax_ok(script, -1, 0, "zero", -2);
+       check_table_get_uintmax_ok(script, -1, 1, "small-positive-int", -1);
+       check_table_get_uintmax_err(script, -1, -1, "small-negative-int", 0);
+       check_table_get_uintmax_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+       check_table_get_uintmax_err(script, -1, -1, "large-negative-int", 2);
+#if LUA_VERSION_NUM != 502
+       check_table_get_uintmax_err(script, -1, -1, "small-float", 3);
+#else
+       check_table_get_uintmax_ok(script, -1, 1, "small-float", 3);
+#endif
+       check_table_get_uintmax_err(script, -1, -1, "bool-true", 4);
+       check_table_get_uintmax_err(script, -1, -1, "bool-false", 5);
+       check_table_get_uintmax_err(script, -1, -1, "str", 6);
+
+       /* lua_Number */
+       check_table_get_number_ok(script, -1, 0, "zero", -2);
+       check_table_get_number_ok(script, -1, 1, "small-positive-int", -1);
+       check_table_get_number_ok(script, -1, -5, "small-negative-int", 0);
+       check_table_get_number_ok(script, -1, 1ll<<48, "large-positive-int", 1);
+       check_table_get_number_ok(script, -1, -(1ll<<48), "large-negative-int", 2);
+       check_table_get_number_ok(script, -1, 1.525, "small-float", 3);
+       check_table_get_number_err(script, -1, -1, "bool-true", 4);
+       check_table_get_number_err(script, -1, -1, "bool-false", 5);
+       check_table_get_number_err(script, -1, -1, "str", 6);
+
+       /* bool */
+       check_table_get_bool_err(script, -1, -1, "zero", -2);
+       check_table_get_bool_err(script, -1, -1, "small-positive-int", -1);
+       check_table_get_bool_err(script, -1, -1, "small-negative-int", 0);
+       check_table_get_bool_err(script, -1, -1, "large-positive-int", 1);
+       check_table_get_bool_err(script, -1, -1, "large-negative-int", 2);
+       check_table_get_bool_err(script, -1, -1, "small-float", 3);
+       check_table_get_bool_ok(script, -1, TRUE, "bool-true", 4);
+       check_table_get_bool_ok(script, -1, FALSE, "bool-false", 5);
+       check_table_get_bool_err(script, -1, -1, "str", 6);
+
+       /* const char * */
+       check_table_get_string_err(script, -1, -1, "zero", -2);
+       check_table_get_string_err(script, -1, -1, "small-positive-int", -1);
+       check_table_get_string_err(script, -1, -1, "small-negative-int", 0);
+       check_table_get_string_err(script, -1, -1, "large-positive-int", 1);
+       check_table_get_string_err(script, -1, -1, "large-negative-int", 2);
+       check_table_get_string_err(script, -1, -1, "small-float", 3);
+       check_table_get_string_err(script, -1, -1, "bool-true", 4);
+       check_table_get_string_err(script, -1, -1, "bool-false", 5);
+       check_table_get_string_ok(script, -1, "string", "str", 6);
+
+       check_table_missing(script, -1, "missing", -10);
+
+       lua_pop(script->L, 1);
+
        dlua_script_unref(&script);
 
        test_end();