From e790ebee8ad78e91fc61738c5c40062ed36b1d44 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 3 Jan 2021 16:12:38 +0100
Subject: Fixed a memory leak when a Python plugin fails to load.

---
 plugins/pychrysalide/core.c   | 31 +++++++++++--------------------
 plugins/pychrysalide/plugin.c | 33 ++++++++++++++++++++++++++++++++-
 2 files changed, 43 insertions(+), 21 deletions(-)

diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c
index a148654..7bc46e4 100644
--- a/plugins/pychrysalide/core.c
+++ b/plugins/pychrysalide/core.c
@@ -664,6 +664,7 @@ static void load_python_plugins(GPluginModule *plugin)
     struct dirent *entry;                   /* Elément trouvé              */
     char *modname;                          /* Nom du module pour Python   */
     char *filename;                         /* Chemin d'accès reconstruit  */
+    PyThreadState *tstate;                  /* Contexte d'environnement    */
     GPluginModule *pyplugin;                /* Lien vers un grffon Python  */
     bool status;                            /* Bilan d'une opération       */
     GGenConfig *config;                     /* Configuration à charger     */
@@ -743,8 +744,17 @@ static void load_python_plugins(GPluginModule *plugin)
             filename = stradd(filename, G_DIR_SEPARATOR_S);
             filename = stradd(filename, entry->d_name);
 
+            if (!_standalone)
+            {
+                tstate = get_pychrysalide_main_tstate();
+                PyEval_RestoreThread(tstate);
+            }
+
             pyplugin = g_python_plugin_new(modname, filename);
 
+            if (!_standalone)
+                PyEval_SaveThread();
+
             if (pyplugin == NULL)
             {
                 g_plugin_module_log_variadic_message(plugin, LMT_ERROR, 
@@ -761,6 +771,7 @@ static void load_python_plugins(GPluginModule *plugin)
             {
                 g_plugin_module_log_variadic_message(plugin, LMT_ERROR,
                                                      _("Plugin '%s' failed to complete loading..."), filename);
+                g_object_unref(G_OBJECT(pyplugin));
                 goto done_with_plugin;
             }
 
@@ -772,12 +783,6 @@ static void load_python_plugins(GPluginModule *plugin)
                                                  _("Loaded the Python plugin found in the '<b>%s</b>' directory"),
                                                  filename);
 
-            /**
-             * Comme le greffon n'est pas passé par la résolution des dépendances,
-             * on simule l'effet attendu.
-             */
-            g_object_ref(G_OBJECT(plugin));
-
             _register_plugin(pyplugin);
 
  done_with_plugin:
@@ -894,23 +899,9 @@ G_MODULE_EXPORT void chrysalide_plugin_exit(GPluginModule *plugin)
 
 G_MODULE_EXPORT void chrysalide_plugin_on_plugins_loaded(GPluginModule *plugin, PluginAction action)
 {
-    PyThreadState *tstate;                  /* Contexte d'environnement    */
-
     if (action == PGA_NATIVE_PLUGINS_LOADED)
-    {
-        if (!_standalone)
-        {
-            tstate = get_pychrysalide_main_tstate();
-            PyEval_RestoreThread(tstate);
-        }
-
         load_python_plugins(plugin);
 
-        if (!_standalone)
-            PyEval_SaveThread();
-
-    }
-
 }
 
 
diff --git a/plugins/pychrysalide/plugin.c b/plugins/pychrysalide/plugin.c
index aa91fb8..88b20de 100644
--- a/plugins/pychrysalide/plugin.c
+++ b/plugins/pychrysalide/plugin.c
@@ -34,6 +34,7 @@
 
 #include <common/extstr.h>
 #include <plugins/dt.h>
+#include <plugins/pglist.h>
 #include <plugins/self.h>
 
 
@@ -274,6 +275,7 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
     int ret;                                /* Bilan d'un appel            */
     GPluginModule *plugin;                  /* Greffon à manipuler         */
     plugin_interface *iface;                /* Interface à constituer      */
+    GPluginModule *dependency;              /* Module nécessaire           */
     PyObject *value;                        /* Valeur à présence imposée   */
     size_t i;                               /* Boucle de parcours          */
     PyObject *action;                       /* Identifiant d'une action    */
@@ -353,10 +355,37 @@ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds)
 
     iface->container = false;
 
+    /**
+     * Comme le greffon n'est pas passé par la résolution des dépendances,
+     * orchestrée par la fonction g_plugin_module_resolve_dependencies(),
+     * on simule l'effet attendu en obtenant une référence par un appel à
+     * get_plugin_by_name().
+     *
+     * L'incrémentation des références doit coller au plus près de
+     * l'inscription nominative du greffon : en cas de sortie impromptue
+     * (lorsqu'une erreur intervient pendant un chargement par exemple),
+     * l'état de l'ensemble est ainsi cohérent au moment du retrait du
+     * greffon fautif via la fonction g_plugin_module_dispose().
+     */
+
+    lock_plugin_list_for_reading();
+    dependency = get_plugin_by_name("PyChrysalide", NULL);
+    unlock_plugin_list_for_reading();
+
+    assert(dependency != NULL);
+
+    if (dependency == NULL)
+    {
+        PyErr_SetString(PyExc_TypeError, _("The internal name of the Python plugin has changed!"));
+        return -1;
+    }
+
     iface->required = malloc(sizeof(char *));
     iface->required[0] = "PyChrysalide";
     iface->required_count = 1;
 
+    /* Validation du reste de l'interface */
+
     value = PyObject_GetAttrString(self, "_actions");
 
     if (value == NULL)
@@ -1331,6 +1360,7 @@ static void g_python_plugin_init(GPythonPlugin *plugin)
 
 static void g_python_plugin_dispose(GPythonPlugin *plugin)
 {
+#if 0
     PyThreadState *tstate;                  /* Contexte d'environnement    */
 
     /**
@@ -1361,6 +1391,7 @@ static void g_python_plugin_dispose(GPythonPlugin *plugin)
 
     if (tstate != NULL)
         PyEval_SaveThread();
+#endif
 
     G_OBJECT_CLASS(g_python_plugin_parent_class)->dispose(G_OBJECT(plugin));
 
@@ -1392,7 +1423,7 @@ static void g_python_plugin_finalize(GPythonPlugin *plugin)
         if (final->version != NULL) free(final->version);
         if (final->url != NULL) free(final->url);
 
-        assert(final->required_count <= 1);
+        assert(final->required_count == 1);
 
         if (final->required != NULL)
             free(final->required);
-- 
cgit v0.11.2-87-g4458