From 340cbdbc4abedd060f3eb6745cd44e33ed19b93c Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Thu, 15 Aug 2019 10:01:39 +0200
Subject: Introduced binary symbol subclassing from Python.

---
 plugins/pychrysalide/format/symbol.c | 236 +++++++++++++++++++++++++++++------
 plugins/pychrysalide/helpers.c       |  31 +++++
 plugins/pychrysalide/helpers.h       |   3 +
 tests/format/symbol.py               |  29 +++++
 4 files changed, 264 insertions(+), 35 deletions(-)

diff --git a/plugins/pychrysalide/format/symbol.c b/plugins/pychrysalide/format/symbol.c
index 86a321c..962c627 100644
--- a/plugins/pychrysalide/format/symbol.c
+++ b/plugins/pychrysalide/format/symbol.c
@@ -27,13 +27,15 @@
 
 #include <assert.h>
 #include <malloc.h>
+#include <string.h>
 #include <pygobject.h>
 
 
 #include <i18n.h>
 
 
-#include <format/symbol.h>
+#include <format/symbol-int.h>
+#include <plugins/dt.h>
 
 
 #include "constants.h"
@@ -47,12 +49,29 @@
 
 
 
-/* Effectue une comparaison avec un objet Python 'BinSymbol'. */
-static PyObject *py_binary_symbol_richcompare(PyObject *, PyObject *, int);
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
 
-/* Crée un nouvel objet Python de type 'BinSymbol'. */
+/* Accompagne la création d'une instance dérivée en Python. */
 static PyObject *py_binary_symbol_new(PyTypeObject *, PyObject *, PyObject *);
 
+/* Initialise la classe des symboles d'exécutables. */
+static void py_binary_symbol_init_gclass(GBinSymbolClass *, gpointer);
+
+/* Fournit une étiquette pour viser un symbole. */
+static char *g_binary_symbol_get_label_wrapper(const GBinSymbol *);
+
+/* Initialise une instance sur la base du dérivé de GObject. */
+static int py_binary_symbol_init(PyObject *, PyObject *, PyObject *);
+
+
+
+/* --------------------- FONCTIONNALITES BASIQUES POUR SYMBOLES --------------------- */
+
+
+/* Effectue une comparaison avec un objet Python 'BinSymbol'. */
+static PyObject *py_binary_symbol_richcompare(PyObject *, PyObject *, int);
+
 /* Fournit l'emplacement où se situe un symbole. */
 static PyObject *py_binary_symbol_get_range(PyObject *, void *);
 
@@ -79,45 +98,129 @@ static int py_binary_symbol_set_label(PyObject *, PyObject *, void *);
 
 
 
+/* ---------------------------------------------------------------------------------- */
+/*                          GLUE POUR CREATION DEPUIS PYTHON                          */
+/* ---------------------------------------------------------------------------------- */
+
+
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : a  = premier object Python à consulter.                      *
-*                b  = second object Python à consulter.                       *
-*                op = type de comparaison menée.                              *
+*  Paramètres  : type = type du nouvel objet à mettre en place.               *
+*                args = éventuelle liste d'arguments.                         *
+*                kwds = éventuel dictionnaire de valeurs mises à disposition. *
 *                                                                             *
-*  Description : Effectue une comparaison avec un objet Python 'BinSymbol'.   *
+*  Description : Accompagne la création d'une instance dérivée en Python.     *
 *                                                                             *
-*  Retour      : Bilan de l'opération.                                        *
+*  Retour      : Nouvel objet Python mis en place ou NULL en cas d'échec.     *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_binary_symbol_richcompare(PyObject *a, PyObject *b, int op)
+static PyObject *py_binary_symbol_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-    PyObject *result;                       /* Bilan à retourner           */
-    int ret;                                /* Bilan de lecture des args.  */
-    const GBinSymbol *sym_a;                /* Premier élément à traiter   */
-    const GBinSymbol *sym_b;                /* Second élément à traiter    */
-    int status;                             /* Résultat d'une comparaison  */
+    PyObject *result;                       /* Objet à retourner           */
+    PyTypeObject *base;                     /* Type de base à dériver      */
+    bool first_time;                        /* Evite les multiples passages*/
+    GType gtype;                            /* Nouveau type de processeur  */
+    bool status;                            /* Bilan d'un enregistrement   */
 
-    ret = PyObject_IsInstance(b, (PyObject *)get_python_binary_symbol_type());
-    if (!ret)
+    /* Validations diverses */
+
+    base = get_python_binary_symbol_type();
+
+    if (type == base)
+        goto simple_way;
+
+    /* Mise en place d'un type dédié */
+
+    first_time = (g_type_from_name(type->tp_name) == 0);
+
+    gtype = build_dynamic_type(G_TYPE_BIN_SYMBOL, type->tp_name,
+                               (GClassInitFunc)py_binary_symbol_init_gclass, NULL, NULL);
+
+    if (first_time)
     {
-        result = Py_NotImplemented;
-        goto cmp_done;
+        status = register_class_for_dynamic_pygobject(gtype, type, base);
+
+        if (!status)
+        {
+            result = NULL;
+            goto exit;
+        }
+
     }
 
-    sym_a = G_BIN_SYMBOL(pygobject_get(a));
-    sym_b = G_BIN_SYMBOL(pygobject_get(b));
+    /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */
 
-    status = g_binary_symbol_cmp(&sym_a, &sym_b);
+ simple_way:
 
-    result = status_to_rich_cmp_state(status, op);
+    result = PyType_GenericNew(type, args, kwds);
 
- cmp_done:
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : class  = classe à initialiser.                               *
+*                unused = données non utilisées ici.                          *
+*                                                                             *
+*  Description : Initialise la classe des symboles d'exécutables.             *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_binary_symbol_init_gclass(GBinSymbolClass *class, gpointer unused)
+{
+    class->get_label = g_binary_symbol_get_label_wrapper;
+
+}
 
-    Py_INCREF(result);
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : symbol = symbole à venir consulter.                          *
+*                                                                             *
+*  Description : Fournit une étiquette pour viser un symbole.                 *
+*                                                                             *
+*  Retour      : Chaîne de caractères renvoyant au symbole.                   *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static char *g_binary_symbol_get_label_wrapper(const GBinSymbol *symbol)
+{
+    char *result;                           /* Etiquette à retourner       */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *pyret;                        /* Bilan de consultation       */
+
+    result = NULL;
+
+    pyobj = pygobject_new(G_OBJECT(symbol));
+
+    if (has_python_method(pyobj, "_get_label"))
+    {
+        pyret = run_python_method(pyobj, "_get_label", NULL);
+        if (pyret == NULL) goto exit;
+
+        if (PyUnicode_Check(pyret))
+            result = strdup(PyUnicode_DATA(pyret));
+
+        Py_DECREF(pyret);
+
+    }
+
+ exit:
+
+    Py_DECREF(pyobj);
 
     return result;
 
@@ -126,21 +229,20 @@ static PyObject *py_binary_symbol_richcompare(PyObject *a, PyObject *b, int op)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : type = type de l'objet à instancier.                         *
+*  Paramètres  : self = objet à initialiser (théoriquement).                  *
 *                args = arguments fournis à l'appel.                          *
 *                kwds = arguments de type key=val fournis.                    *
 *                                                                             *
-*  Description : Crée un nouvel objet Python de type 'BinSymbol'.             *
+*  Description : Initialise une instance sur la base du dérivé de GObject.    *
 *                                                                             *
-*  Retour      : Instance Python mise en place.                               *
+*  Retour      : 0.                                                           *
 *                                                                             *
 *  Remarques   : -                                                            *
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_binary_symbol_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
+static int py_binary_symbol_init(PyObject *self, PyObject *args, PyObject *kwds)
 {
-    PyObject *result;                       /* Bilan à retourner           */
     SymbolType stype;                       /* Type prévu pour le  symbole */
     mrange_t range;                         /* Version native d'un espace  */
     int ret;                                /* Bilan de lecture des args.  */
@@ -157,20 +259,82 @@ static PyObject *py_binary_symbol_new(PyTypeObject *type, PyObject *args, PyObje
     "\n"                                                                        \
     "Where stype is a pychrysalide.format.BinSymbol.SymbolType value, and"      \
     " range a memory space defined by pychrysalide.arch.mrange."                \
+    "\n"                                                                        \
+    "The following special method can be overridden:\n"                         \
+    "* _get_label(self): provides a default label for the symbol."
+
+    /* Récupération des paramètres */
 
     ret = PyArg_ParseTuple(args, "kO&", &stype, convert_any_to_mrange, &range);
-    if (!ret) return NULL;
+    if (!ret) return -1;
 
     if (stype >= STP_COUNT)
     {
         PyErr_SetString(PyExc_ValueError, _("Invalid type of symbol."));
-        return NULL;
+        return -1;
     }
 
-    symbol = g_binary_symbol_new(&range, stype);
+    /* Initialisation d'un objet GLib */
+
+    ret = forward_pygobjet_init(self);
+    if (ret == -1) return -1;
+
+    /* Eléments de base */
+
+    symbol = G_BIN_SYMBOL(pygobject_get(self));
+
+    g_binary_symbol_set_range(symbol, &range);
+    g_binary_symbol_set_stype(symbol, stype);
+
+    return 0;
+
+}
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                       FONCTIONNALITES BASIQUES POUR SYMBOLES                       */
+/* ---------------------------------------------------------------------------------- */
 
-    result = pygobject_new(G_OBJECT(symbol));
-    g_object_unref(symbol);
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : a  = premier object Python à consulter.                      *
+*                b  = second object Python à consulter.                       *
+*                op = type de comparaison menée.                              *
+*                                                                             *
+*  Description : Effectue une comparaison avec un objet Python 'BinSymbol'.   *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_binary_symbol_richcompare(PyObject *a, PyObject *b, int op)
+{
+    PyObject *result;                       /* Bilan à retourner           */
+    int ret;                                /* Bilan de lecture des args.  */
+    const GBinSymbol *sym_a;                /* Premier élément à traiter   */
+    const GBinSymbol *sym_b;                /* Second élément à traiter    */
+    int status;                             /* Résultat d'une comparaison  */
+
+    ret = PyObject_IsInstance(b, (PyObject *)get_python_binary_symbol_type());
+    if (!ret)
+    {
+        result = Py_NotImplemented;
+        goto cmp_done;
+    }
+
+    sym_a = G_BIN_SYMBOL(pygobject_get(a));
+    sym_b = G_BIN_SYMBOL(pygobject_get(b));
+
+    status = g_binary_symbol_cmp(&sym_a, &sym_b);
+
+    result = status_to_rich_cmp_state(status, op);
+
+ cmp_done:
+
+    Py_INCREF(result);
 
     return result;
 
@@ -507,6 +671,8 @@ PyTypeObject *get_python_binary_symbol_type(void)
 
         .tp_methods     = py_bin_symbol_methods,
         .tp_getset      = py_bin_symbol_getseters,
+
+        .tp_init        = py_binary_symbol_init,
         .tp_new         = py_binary_symbol_new
 
     };
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index 6e13d3c..59bcd45 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -941,6 +941,37 @@ bool register_class_for_dynamic_pygobject(GType gtype, PyTypeObject *type, PyTyp
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : self = objet Python/GObject à initialiser.                   *
+*                                                                             *
+*  Description : Fait suivre à la partie GObject une initialisation nouvelle. *
+*                                                                             *
+*  Retour      : Bilan de l'opération.                                        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int forward_pygobjet_init(PyObject *self)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *new_args;                     /* Nouveaux arguments épurés   */
+    PyObject *new_kwds;                     /* Nouveau dictionnaire épuré  */
+
+    new_args = PyTuple_New(0);
+    new_kwds = PyDict_New();
+
+    result = PyGObject_Type.tp_init(self, new_args, new_kwds);
+
+    Py_DECREF(new_kwds);
+    Py_DECREF(new_args);
+
+    return result;
+
+}
+
+
 
 /* ---------------------------------------------------------------------------------- */
 /*                         TRANSFERT DES VALEURS CONSTANTES                           */
diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h
index e2ebfbc..8c7db0b 100644
--- a/plugins/pychrysalide/helpers.h
+++ b/plugins/pychrysalide/helpers.h
@@ -154,6 +154,9 @@ bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *);
 /* Enregistre un type Python dérivant d'un type GLib dynamique. */
 bool register_class_for_dynamic_pygobject(GType, PyTypeObject *, PyTypeObject *);
 
+/* Fait suivre à la partie GObject une initialisation nouvelle. */
+int forward_pygobjet_init(PyObject *);
+
 
 
 /* ----------------------- TRANSFERT DES VALEURS CONSTANTES ------------------------- */
diff --git a/tests/format/symbol.py b/tests/format/symbol.py
index 5e0ba10..6cffe57 100644
--- a/tests/format/symbol.py
+++ b/tests/format/symbol.py
@@ -84,3 +84,32 @@ class TestBinarySymbols(ChrysalideTestCase):
         self.assertTrue(symbol1 < symbol2)
 
         self.assertTrue(symbol0 == symbol2)
+
+
+    def testSymbolSubclassing(self):
+        """Verify the symbol subclassing is working."""
+
+        class MySymbol(BinSymbol):
+            def _get_label(self):
+                return 'AAA'
+
+        saddr = vmpa(0x100, vmpa.VMPA_NO_VIRTUAL)
+        srange = mrange(saddr, 0x3)
+        symbol = MySymbol(BinSymbol.SymbolType.ENTRY_POINT, srange)
+
+        self.assertEqual(symbol.label, 'AAA')
+
+        symbol.label = 'BBB'
+
+        self.assertEqual(symbol.label, 'BBB')
+
+        class MyOtherSymbol(BinSymbol):
+            pass
+
+        other = MyOtherSymbol(BinSymbol.SymbolType.ENTRY_POINT, srange)
+
+        self.assertEqual(other.label, None)
+
+        other.label = 'CCC'
+
+        self.assertEqual(other.label, 'CCC')
-- 
cgit v0.11.2-87-g4458