[Libguestfs] [PATCH libnbd 2/6] generator: Create only one Python wrapper per closure.

Richard W.M. Jones rjones at redhat.com
Tue Aug 13 10:06:17 UTC 2019


We were previously generating one instance of the Python closure
wrapper per (function * Closure arg).  However these wrappers didn't
actually differ across functions.  We can therefore save a lot of code
by only generating one wrapper per closure globally.

This reduces the amount of generated code by nearly 25%.  Before and
after:

$ wc -l python/methods.c
3275 python/methods.c

$ wc -l python/methods.c
2662 python/methods.c
---
 generator/generator | 237 ++++++++++++++++++++++----------------------
 1 file changed, 117 insertions(+), 120 deletions(-)

diff --git a/generator/generator b/generator/generator
index 9dbef2a..a031bd0 100755
--- a/generator/generator
+++ b/generator/generator
@@ -4108,126 +4108,122 @@ PyInit_libnbdmod (void)
 }
 "
 
+(* Functions with a Closure parameter are special because we
+ * have to generate wrapper functions which translate the
+ * callbacks back to Python.
+ *)
+let print_python_closure_wrapper { cbname; cbargs } =
+  pr "/* Wrapper for %s callback. */\n" cbname;
+  pr "static int\n";
+  pr "%s_wrapper " cbname;
+  C.print_cbarg_list cbargs;
+  pr "\n";
+  pr "{\n";
+  pr "  int ret = 0;\n";
+  pr "\n";
+  pr "  if (valid_flag & LIBNBD_CALLBACK_VALID) {\n";
+  pr "    PyGILState_STATE py_save = PyGILState_UNLOCKED;\n";
+  pr "    PyObject *py_args, *py_ret;\n";
+  List.iter (
+    function
+    | CBArrayAndLen (UInt32 n, len) ->
+       pr "    PyObject *py_%s = PyList_New (%s);\n" n len;
+       pr "    for (size_t i = 0; i < %s; ++i)\n" len;
+       pr "      PyList_SET_ITEM (py_%s, i, PyLong_FromUnsignedLong (%s[i]));\n" n n
+    | CBBytesIn _
+    | CBInt _
+    | CBInt64 _ -> ()
+    | CBMutable (Int n) ->
+       pr "    PyObject *py_%s_modname = PyUnicode_FromString (\"ctypes\");\n" n;
+       pr "    if (!py_%s_modname) { PyErr_PrintEx (0); return -1; }\n" n;
+       pr "    PyObject *py_%s_mod = PyImport_Import (py_%s_modname);\n" n n;
+       pr "    Py_DECREF (py_%s_modname);\n" n;
+       pr "    if (!py_%s_mod) { PyErr_PrintEx (0); return -1; }\n" n;
+       pr "    PyObject *py_%s = PyObject_CallMethod (py_%s_mod, \"c_int\", \"i\", *%s);\n" n n n;
+       pr "    if (!py_%s) { PyErr_PrintEx (0); return -1; }\n" n;
+    | CBString _
+    | CBUInt _
+    | CBUInt64 _ -> ()
+    | CBArrayAndLen _ | CBMutable _ -> assert false
+  ) cbargs;
+  pr "\n";
+
+  pr "    py_args = Py_BuildValue (\"(\"";
+  List.iter (
+    function
+    | CBArrayAndLen (UInt32 n, len) -> pr " \"O\""
+    | CBBytesIn (n, len) -> pr " \"y#\""
+    | CBInt n -> pr " \"i\""
+    | CBInt64 n -> pr " \"L\""
+    | CBMutable (Int n) -> pr " \"O\""
+    | CBString n -> pr " \"s\""
+    | CBUInt n -> pr " \"I\""
+    | CBUInt64 n -> pr " \"K\""
+    | CBArrayAndLen _ | CBMutable _ -> assert false
+  ) cbargs;
+  pr " \")\"";
+  List.iter (
+    function
+    | CBArrayAndLen (UInt32 n, _) -> pr ", py_%s" n
+    | CBBytesIn (n, len) -> pr ", %s, (int) %s" n len
+    | CBMutable (Int n) -> pr ", py_%s" n
+    | CBInt n | CBInt64 n
+    | CBString n
+    | CBUInt n | CBUInt64 n -> pr ", %s" n
+    | CBArrayAndLen _ | CBMutable _ -> assert false
+  ) cbargs;
+  pr ");\n";
+  pr "    Py_INCREF (py_args);\n";
+  pr "\n";
+  pr "    if (PyEval_ThreadsInitialized ())\n";
+  pr "      py_save = PyGILState_Ensure ();\n";
+  pr "\n";
+  pr "    py_ret = PyObject_CallObject ((PyObject *)user_data, py_args);\n";
+  pr "\n";
+  pr "    if (PyEval_ThreadsInitialized ())\n";
+  pr "      PyGILState_Release (py_save);\n";
+  pr "\n";
+  pr "    Py_DECREF (py_args);\n";
+  pr "\n";
+  pr "    if (py_ret != NULL) {\n";
+  pr "      if (PyLong_Check (py_ret))\n";
+  pr "        ret = PyLong_AsLong (py_ret);\n";
+  pr "      else\n";
+  pr "        /* If it's not a long, just assume it's 0. */\n";
+  pr "        ret = 0;\n";
+  pr "      Py_DECREF (py_ret);\n";
+  pr "    }\n";
+  pr "    else {\n";
+  pr "      ret = -1;\n";
+  pr "      PyErr_PrintEx (0); /* print exception */\n";
+  pr "    };\n";
+  pr "\n";
+  List.iter (
+    function
+    | CBArrayAndLen (UInt32 n, _) ->
+       pr "    Py_DECREF (py_%s);\n" n
+    | CBMutable (Int n) ->
+       pr "    PyObject *py_%s_ret = PyObject_GetAttrString (py_%s, \"value\");\n" n n;
+       pr "    *%s = PyLong_AsLong (py_%s_ret);\n" n n;
+       pr "    Py_DECREF (py_%s_ret);\n" n;
+       pr "    Py_DECREF (py_%s);\n" n
+    | CBBytesIn _
+    | CBInt _ | CBInt64 _
+    | CBString _
+    | CBUInt _ | CBUInt64 _ -> ()
+    | CBArrayAndLen _ | CBMutable _ -> assert false
+  ) cbargs;
+  pr "  }\n";
+  pr "\n";
+  pr "  if (valid_flag & LIBNBD_CALLBACK_FREE)\n";
+  pr "    Py_DECREF ((PyObject *)user_data);\n";
+  pr "\n";
+  pr "  return ret;\n";
+  pr "}\n";
+  pr "\n"
+
+(* Generate the Python binding. *)
 let print_python_binding name { args; optargs; ret; may_set_error } =
-  (* Functions with a Closure parameter are special because we
-   * have to generate wrapper functions which translate the
-   * callbacks back to Python.
-   *)
-  List.iter (
-    function
-    | Closure { cbname; cbargs } ->
-       pr "/* Wrapper for %s callback of %s. */\n" cbname name;
-       pr "static int\n";
-       pr "%s_%s_wrapper " name cbname;
-       C.print_cbarg_list cbargs;
-       pr "\n";
-       pr "{\n";
-       pr "  int ret = 0;\n";
-       pr "\n";
-       pr "  if (valid_flag & LIBNBD_CALLBACK_VALID) {\n";
-       pr "    PyGILState_STATE py_save = PyGILState_UNLOCKED;\n";
-       pr "    PyObject *py_args, *py_ret;\n";
-       List.iter (
-         function
-         | CBArrayAndLen (UInt32 n, len) ->
-            pr "    PyObject *py_%s = PyList_New (%s);\n" n len;
-            pr "    for (size_t i = 0; i < %s; ++i)\n" len;
-            pr "      PyList_SET_ITEM (py_%s, i, PyLong_FromUnsignedLong (%s[i]));\n" n n
-         | CBBytesIn _
-         | CBInt _
-         | CBInt64 _ -> ()
-         | CBMutable (Int n) ->
-            pr "    PyObject *py_%s_modname = PyUnicode_FromString (\"ctypes\");\n" n;
-            pr "    if (!py_%s_modname) { PyErr_PrintEx (0); return -1; }\n" n;
-            pr "    PyObject *py_%s_mod = PyImport_Import (py_%s_modname);\n" n n;
-            pr "    Py_DECREF (py_%s_modname);\n" n;
-            pr "    if (!py_%s_mod) { PyErr_PrintEx (0); return -1; }\n" n;
-            pr "    PyObject *py_%s = PyObject_CallMethod (py_%s_mod, \"c_int\", \"i\", *%s);\n" n n n;
-            pr "    if (!py_%s) { PyErr_PrintEx (0); return -1; }\n" n;
-         | CBString _
-         | CBUInt _
-         | CBUInt64 _ -> ()
-         | CBArrayAndLen _ | CBMutable _ -> assert false
-       ) cbargs;
-       pr "\n";
-
-       pr "    py_args = Py_BuildValue (\"(\"";
-       List.iter (
-         function
-         | CBArrayAndLen (UInt32 n, len) -> pr " \"O\""
-         | CBBytesIn (n, len) -> pr " \"y#\""
-         | CBInt n -> pr " \"i\""
-         | CBInt64 n -> pr " \"L\""
-         | CBMutable (Int n) -> pr " \"O\""
-         | CBString n -> pr " \"s\""
-         | CBUInt n -> pr " \"I\""
-         | CBUInt64 n -> pr " \"K\""
-         | CBArrayAndLen _ | CBMutable _ -> assert false
-       ) cbargs;
-       pr " \")\"";
-       List.iter (
-         function
-         | CBArrayAndLen (UInt32 n, _) -> pr ", py_%s" n
-         | CBBytesIn (n, len) -> pr ", %s, (int) %s" n len
-         | CBMutable (Int n) -> pr ", py_%s" n
-         | CBInt n | CBInt64 n
-         | CBString n
-         | CBUInt n | CBUInt64 n -> pr ", %s" n
-         | CBArrayAndLen _ | CBMutable _ -> assert false
-       ) cbargs;
-       pr ");\n";
-       pr "    Py_INCREF (py_args);\n";
-       pr "\n";
-       pr "    if (PyEval_ThreadsInitialized ())\n";
-       pr "      py_save = PyGILState_Ensure ();\n";
-       pr "\n";
-       pr "    py_ret = PyObject_CallObject ((PyObject *)user_data, py_args);\n";
-       pr "\n";
-       pr "    if (PyEval_ThreadsInitialized ())\n";
-       pr "      PyGILState_Release (py_save);\n";
-       pr "\n";
-       pr "    Py_DECREF (py_args);\n";
-       pr "\n";
-       pr "    if (py_ret != NULL) {\n";
-       pr "      if (PyLong_Check (py_ret))\n";
-       pr "        ret = PyLong_AsLong (py_ret);\n";
-       pr "      else\n";
-       pr "        /* If it's not a long, just assume it's 0. */\n";
-       pr "        ret = 0;\n";
-       pr "      Py_DECREF (py_ret);\n";
-       pr "    }\n";
-       pr "    else {\n";
-       pr "      ret = -1;\n";
-       pr "      PyErr_PrintEx (0); /* print exception */\n";
-       pr "    };\n";
-       pr "\n";
-       List.iter (
-         function
-         | CBArrayAndLen (UInt32 n, _) ->
-            pr "    Py_DECREF (py_%s);\n" n
-         | CBMutable (Int n) ->
-            pr "    PyObject *py_%s_ret = PyObject_GetAttrString (py_%s, \"value\");\n" n n;
-            pr "    *%s = PyLong_AsLong (py_%s_ret);\n" n n;
-            pr "    Py_DECREF (py_%s_ret);\n" n;
-            pr "    Py_DECREF (py_%s);\n" n
-         | CBBytesIn _
-         | CBInt _ | CBInt64 _
-         | CBString _
-         | CBUInt _ | CBUInt64 _ -> ()
-         | CBArrayAndLen _ | CBMutable _ -> assert false
-       ) cbargs;
-       pr "  }\n";
-       pr "\n";
-       pr "  if (valid_flag & LIBNBD_CALLBACK_FREE)\n";
-       pr "    Py_DECREF ((PyObject *)user_data);\n";
-       pr "\n";
-       pr "  return ret;\n";
-       pr "}\n";
-       pr "\n"
-    | _ -> ()
-  ) args;
-
-  (* Generate the Python binding. *)
   pr "PyObject *\n";
   pr "nbd_internal_py_%s (PyObject *self, PyObject *args)\n" name;
   pr "{\n";
@@ -4390,7 +4386,7 @@ let print_python_binding name { args; optargs; ret; may_set_error } =
     | BytesPersistIn (n, _)
     | BytesPersistOut (n, _) -> pr ", %s_buf->data, %s_buf->len" n n
     | Closure { cbname } ->
-       pr ", %s_%s_wrapper" name cbname;
+       pr ", %s_wrapper" cbname;
        pr ", %s_user_data" cbname
     | Enum (n, _) -> pr ", %s" n
     | Flags (n, _) -> pr ", %s_u32" n
@@ -4503,6 +4499,7 @@ let generate_python_methods_c () =
   pr "\n";
   pr "#include <methods.h>\n";
   pr "\n";
+  List.iter print_python_closure_wrapper all_closures;
   List.iter (
     fun (name, fn) ->
       print_python_binding name fn
-- 
2.22.0




More information about the Libguestfs mailing list