From 5060f746a2ea51e14282b02bd374f9550c925827 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Fri, 29 May 2020 01:29:24 +0200
Subject: Updated the Python bindings for targetable operands.

---
 plugins/pychrysalide/arch/operands/rename.c     |   6 +-
 plugins/pychrysalide/arch/operands/targetable.c | 197 +++++++++++++++++++++++-
 plugins/pychrysalide/arch/operands/targetable.h |   3 +
 3 files changed, 199 insertions(+), 7 deletions(-)

diff --git a/plugins/pychrysalide/arch/operands/rename.c b/plugins/pychrysalide/arch/operands/rename.c
index 7a5be98..60985db 100644
--- a/plugins/pychrysalide/arch/operands/rename.c
+++ b/plugins/pychrysalide/arch/operands/rename.c
@@ -441,12 +441,14 @@ static GRenamedOperand *py_renameable_operand_build_wrapper(const GRenameableOpe
             if (ret == 1)
                 g_object_ref(G_OBJECT(result));
 
-            Py_DECREF(pyret);
+            PyErr_Clear();
 
-            Py_DECREF(args);
+            Py_DECREF(pyret);
 
         }
 
+        Py_DECREF(args);
+
     }
 
     Py_DECREF(pyobj);
diff --git a/plugins/pychrysalide/arch/operands/targetable.c b/plugins/pychrysalide/arch/operands/targetable.c
index d686bcf..78a4617 100644
--- a/plugins/pychrysalide/arch/operands/targetable.c
+++ b/plugins/pychrysalide/arch/operands/targetable.c
@@ -28,7 +28,7 @@
 #include <pygobject.h>
 
 
-#include <arch/operands/targetable.h>
+#include <arch/operands/targetable-int.h>
 
 
 #include "../processor.h"
@@ -39,18 +39,151 @@
 
 
 
+/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */
+
+
+/* Procède à l'initialisation de l'interface pour ciblage. */
+static void py_targetable_operand_init(GTargetableOperandIface *, gpointer *);
+
+/* Obtient l'adresse de la cible visée par un opérande. */
+static bool py_targetable_operand_get_addr_wrapper(const GTargetableOperand *, const vmpa2t *, GBinFormat *, GArchProcessor *, vmpa2t *);
+
+
+
+/* ------------------------ INTERFACE POUR OPERANDE CIBLABLE ------------------------ */
+
+
+/* Obtient l'adresse de la cible visée par un opérande. */
+static PyObject *py_targetable_operand_get_addr(PyObject *, PyObject *);
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                          GLUE POUR CREATION DEPUIS PYTHON                          */
+/* ---------------------------------------------------------------------------------- */
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : iface  = interface GLib à initialiser.                       *
+*                unused = adresse non utilisée ici.                           *
+*                                                                             *
+*  Description : Procède à l'initialisation de l'interface pour ciblage.      *
+*                                                                             *
+*  Retour      : -                                                            *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static void py_targetable_operand_init(GTargetableOperandIface *iface, gpointer *unused)
+{
+
 #define TARGETABLE_OPERAND_DOC                                              \
     "The TargetableOperand interface depicts operands which can drive to"   \
     " another location.\n"                                                  \
     "\n"                                                                    \
     "By instance, an immediate value can target a given address into"       \
-    " some function code."
+    " some function code."                                                  \
+    "\n"                                                                    \
+    "A typical class declaration for a new implementation looks like:\n"    \
+    "\n"                                                                    \
+    "    class NewImplem(GObject.Object, RenamedOperand):\n"                \
+    "        ...\n"                                                         \
+    "\n"                                                                    \
+    "The following method has to be defined for new implementations:\n"     \
+    "* pychrysalide.arch.operands.RenamedOperand._get_addr();\n"            \
 
+    iface->get_addr = py_targetable_operand_get_addr_wrapper;
 
+}
 
-/* Obtient l'adresse de la cible visée par un opérande. */
-static PyObject *py_targetable_operand_get_addr(PyObject *, PyObject *);
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : operand = operande à consulter.                              *
+*                src     = localisation de l'instruction mère.                *
+*                format  = format reconnu pour le binaire chargé.             *
+*                proc    = architecture associée à ce même binaire.           *
+*                addr    = localisation de la cible. [OUT]                    *
+*                                                                             *
+*  Description : Obtient l'adresse de la cible visée par un opérande.         *
+*                                                                             *
+*  Retour      : true si la cible est valide, false sinon.                    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool py_targetable_operand_get_addr_wrapper(const GTargetableOperand *operand, const vmpa2t *src, GBinFormat *format, GArchProcessor *proc, vmpa2t *addr)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyGILState_STATE gstate;                /* Sauvegarde d'environnement  */
+    PyObject *pyobj;                        /* Objet Python concerné       */
+    PyObject *args;                         /* Arguments pour l'appel      */
+    PyObject *pyret;                        /* Bilan de consultation       */
+    int ret;                                /* Bilan d'une conversion      */
+
+#define TARGETABLE_OPERAND_GET_ADDR_WRAPPER PYTHON_WRAPPER_DEF          \
+(                                                                       \
+    _get_addr, "$self, src, format, proc",                              \
+    METH_VARARGS,                                                       \
+    "Abstract method used to compute a target address from an operand." \
+    "\n"                                                                \
+    "The *src* argument is the location of the instruction owning the"  \
+    " operand, as a pychrysalide.arch.vmpa instance. The format is a"   \
+    " pychrysalide.format.BinFormat instance, providing all needed"     \
+    " information and the processor is a"                               \
+    " pychrysalide.arch.ArchProcessor instance, providing all needed"   \
+    " information too.\n"                                               \
+    "\n"                                                                \
+    "The result has to be a pychrysalide.arch.vmpa address or None."    \
+)
+
+    result = false;
+
+    gstate = PyGILState_Ensure();
+
+    pyobj = pygobject_new(G_OBJECT(operand));
+
+    if (has_python_method(pyobj, "_get_addr"))
+    {
+        args = PyTuple_New(3);
+        PyTuple_SetItem(args, 0, build_from_internal_vmpa(src));
+        PyTuple_SetItem(args, 1, pygobject_new(G_OBJECT(format)));
+        PyTuple_SetItem(args, 2, pygobject_new(G_OBJECT(proc)));
+
+        pyret = run_python_method(pyobj, "_get_addr", NULL);
+
+        if (pyret != NULL)
+        {
+            ret = convert_any_to_vmpa(pyret, addr);
+
+            result = (ret == 1);
+
+            PyErr_Clear();
+
+            Py_DECREF(pyret);
+
+        }
+
+        Py_DECREF(args);
+
+    }
+
+    Py_DECREF(pyobj);
+
+    PyGILState_Release(gstate);
+
+    return result;
+
+}
+
+
+
+/* ---------------------------------------------------------------------------------- */
+/*                          INTERFACE POUR OPERANDE CIBLABLE                          */
+/* ---------------------------------------------------------------------------------- */
 
 
 /******************************************************************************
@@ -134,6 +267,7 @@ static PyObject *py_targetable_operand_get_addr(PyObject *self, PyObject *args)
 PyTypeObject *get_python_targetable_operand_type(void)
 {
     static PyMethodDef py_targetable_operand_methods[] = {
+        TARGETABLE_OPERAND_GET_ADDR_WRAPPER,
         TARGETABLE_OPERAND_GET_ADDR_METHOD,
         { NULL }
     };
@@ -181,6 +315,14 @@ bool ensure_python_targetable_operand_is_registered(void)
     PyObject *module;                       /* Module à recompléter        */
     PyObject *dict;                         /* Dictionnaire du module      */
 
+    static GInterfaceInfo info = {          /* Paramètres d'inscription    */
+
+        .interface_init = (GInterfaceInitFunc)py_targetable_operand_init,
+        .interface_finalize = NULL,
+        .interface_data = NULL,
+
+    };
+
     type = get_python_targetable_operand_type();
 
     if (!PyType_HasFeature(type, Py_TPFLAGS_READY))
@@ -189,7 +331,7 @@ bool ensure_python_targetable_operand_is_registered(void)
 
         dict = PyModule_GetDict(module);
 
-        if (!register_interface_for_pygobject(dict, G_TYPE_TARGETABLE_OPERAND, type))
+        if (!register_interface_for_pygobject_2(dict, G_TYPE_TARGETABLE_OPERAND, type, &info))
             return false;
 
     }
@@ -197,3 +339,48 @@ bool ensure_python_targetable_operand_is_registered(void)
     return true;
 
 }
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : arg = argument quelconque à tenter de convertir.             *
+*                dst = destination des valeurs récupérées en cas de succès.   *
+*                                                                             *
+*  Description : Tente de convertir en opérande ciblable.                     *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_targetable_operand(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)get_python_targetable_operand_type());
+
+    switch (result)
+    {
+        case -1:
+            /* L'exception est déjà fixée par Python */
+            result = 0;
+            break;
+
+        case 0:
+            PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to targetable operand");
+            break;
+
+        case 1:
+            *((GTargetableOperand **)dst) = G_TARGETABLE_OPERAND(pygobject_get(arg));
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/arch/operands/targetable.h b/plugins/pychrysalide/arch/operands/targetable.h
index eaa05d2..26d13b9 100644
--- a/plugins/pychrysalide/arch/operands/targetable.h
+++ b/plugins/pychrysalide/arch/operands/targetable.h
@@ -37,6 +37,9 @@ PyTypeObject *get_python_targetable_operand_type(void);
 /* Prend en charge l'objet 'pychrysalide.arch.TargetableOperand'. */
 bool ensure_python_targetable_operand_is_registered(void);
 
+/* Tente de convertir en opérande ciblable. */
+int convert_to_targetable_operand(PyObject *, void *);
+
 
 
 #endif  /* _PLUGINS_PYCHRYSALIDE_ARCH_OPERANDS_TARGETABLE_H */
-- 
cgit v0.11.2-87-g4458