]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-lua: Add dlua_pcall
authorAki Tuomi <aki.tuomi@open-xchange.com>
Fri, 5 Feb 2021 11:33:47 +0000 (13:33 +0200)
committeraki.tuomi <aki.tuomi@open-xchange.com>
Mon, 22 Mar 2021 10:38:20 +0000 (10:38 +0000)
src/lib-lua/dlua-script-private.h
src/lib-lua/dlua-script.c

index 750ff46b2ff738c77f7d08e125f0318b44c0eaed..5b6fa9df0b4beed95cf589b1ac8afa08098a5ba2 100644 (file)
@@ -174,6 +174,18 @@ 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);
 
+/* call a function in a script.
+
+  **NOTE**: This function works differently than lua_pcall:
+
+    return value:
+     -1 = error
+     0+ = number of result(s)
+
+*/
+int dlua_pcall(lua_State *L, const char *func_name, int nargs, int nresults,
+              const char **error_r);
+
 /* dumps current stack as i_debug lines */
 void dlua_dump_stack(lua_State *L);
 
index 79350611254b5af30f88f737b620b5c416543616..c24022b55fd82deb6589d674722e43c3de46513f 100644 (file)
@@ -109,6 +109,82 @@ struct dlua_script *dlua_script_from_state(lua_State *L)
        return script;
 }
 
+int dlua_pcall(lua_State *L, const char *func_name, int nargs, int nresults,
+              const char **error_r)
+{
+       /* record the stack position */
+       int ret = 0, debugh_idx, top = lua_gettop(L) - nargs;
+
+       lua_getglobal(L, func_name);
+
+       if (lua_isfunction(L, -1)) {
+               /* stack on entry
+                       args
+                       func <-- top
+               */
+               /* move func name before arguments */
+               lua_insert(L, -(nargs + 1));
+               /* stack now
+                       func
+                       args <-- top
+               */
+               lua_getglobal(L, "debug");
+               lua_getfield(L, -1, "traceback");
+               lua_replace(L, -2);
+               /* stack now
+                       func
+                       args
+                       traceback <-- top
+               */
+               /* move error handler before func name */
+               lua_insert(L, -(nargs + 2));
+               /* stack now
+                       traceback
+                       func
+                       args <-- top
+               */
+               /* record where traceback is so it's easy to get rid of even
+                  if LUA_MULTRET is used. */
+               debugh_idx = lua_gettop(L) - nargs - 1;
+               ret = lua_pcall(L, nargs, nresults, -(nargs + 2));
+               if (ret != LUA_OK) {
+                       *error_r = t_strdup_printf("lua_pcall(%s, %d, %d) failed: %s",
+                                                  func_name, nargs, nresults,
+                                                  lua_tostring(L, -1));
+                       /* Remove error and debug handler */
+                       lua_pop(L, 2);
+                       ret = -1;
+               } else {
+                       /* remove debug handler from known location */
+                       lua_remove(L, debugh_idx);
+                       if (nresults == LUA_MULTRET)
+                               nresults = lua_gettop(L) - top;
+                       ret = nresults;
+               }
+       } else {
+               /* ensure stack is clean, remove function and arguments */
+               lua_pop(L, nargs + 1);
+               *error_r = t_strdup_printf("'%s' is not a function",
+                                          func_name);
+               ret = -1;
+       }
+#ifdef DEBUG
+       if ((ret == -1 && lua_gettop(L) != top) ||
+           (ret >= 0 &&
+            lua_gettop(L) != top + ret)) {
+               i_debug("LUA STACK UNCLEAN BEGIN for %s", func_name);
+               dlua_dump_stack(L);
+               i_debug("LUA STACK UNCLEAN END");
+       }
+#endif
+       /* enforce that stack is clean after call */
+       if (ret == -1)
+               i_assert(lua_gettop(L) == top);
+       else
+               i_assert(ret >= 0 && lua_gettop(L) == top + ret);
+       return ret;
+}
+
 static int dlua_call_init_function(struct dlua_script *script,
                                   const char **error_r)
 {