From b8a99266e691ec5a2a13f10d6c775f4bdc0dbbc2 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 18 Dec 2018 23:54:36 +0100
Subject: Made Python constant values storable using Pickle.

---
 plugins/pychrysalide/constval.c | 73 ++++++++++++++++++++++++++++++++++++-----
 tests/constval.py               | 30 ++++++++++++++---
 2 files changed, 90 insertions(+), 13 deletions(-)

diff --git a/plugins/pychrysalide/constval.c b/plugins/pychrysalide/constval.c
index b6d20aa..7067770 100644
--- a/plugins/pychrysalide/constval.c
+++ b/plugins/pychrysalide/constval.c
@@ -26,6 +26,7 @@
 
 
 #include <longintrepr.h>
+#include <string.h>
 
 
 #include <i18n.h>
@@ -46,7 +47,7 @@ typedef struct _PyConstvalObject
      * de l'objet PyLongObject, dont la taille est dynamique.
      */
 
-    /*const char *name;*/                   /* Désignation de la valeur    */
+    /*char *name;*/                         /* Désignation de la valeur    */
 
 } PyConstvalObject;
 
@@ -56,11 +57,14 @@ typedef struct _PyConstvalObject
 static PyObject *py_constval_new(PyTypeObject *, PyObject *, PyObject *);
 
 /* Calcule l'emplacement reservé pour une désignation de valeur. */
-static const char **py_constval_compute_name_ptr(PyConstvalObject *);
+static char **py_constval_compute_name_ptr(PyConstvalObject *);
 
 /* Construit une représentation textuelle de l'objet. */
 static PyObject *py_constval_str(PyObject *);
 
+/* Fournit une réduction de l'objet en vue d'une sérialisation. */
+static PyObject *py_constval_reduce(PyObject *, PyObject *);
+
 
 
 /******************************************************************************
@@ -79,9 +83,17 @@ static PyObject *py_constval_str(PyObject *);
 
 static PyObject *py_constval_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
-    PyErr_Format(PyExc_RuntimeError, _("%s is meant to be only used from C code"), type->tp_name);
+    PyObject *result;                       /* Instance à retourner        */
+    unsigned long value;                    /* Valeur entière              */
+    const char *name;                       /* Désignation humaine         */
+    int ret;                                /* Bilan de lecture des args.  */
+
+    ret = PyArg_ParseTuple(args, "ks", &value, &name);
+    if (!ret) return NULL;
+
+    result = build_constval_from_c_code(name, value);
 
-    return NULL;
+    return result;
 
 }
 
@@ -98,7 +110,7 @@ static PyObject *py_constval_new(PyTypeObject *type, PyObject *args, PyObject *k
 *                                                                             *
 ******************************************************************************/
 
-static const char **py_constval_compute_name_ptr(PyConstvalObject *self)
+static char **py_constval_compute_name_ptr(PyConstvalObject *self)
 {
     digit *result;                          /* Adresse mémoire à retourner */
     Py_ssize_t size;                        /* Taille supplémentaire       */
@@ -110,7 +122,7 @@ static const char **py_constval_compute_name_ptr(PyConstvalObject *self)
 
     result = &self->base.ob_digit[size];
 
-    return (const char **)result;
+    return (char **)result;
 
 }
 
@@ -131,7 +143,7 @@ static PyObject *py_constval_str(PyObject *self)
 {
     PyObject *result;                       /* Elément à retourner         */
     PyConstvalObject *constval;             /* Autre version de l'objet    */
-    const char **ptr;                       /* Désignation à convertir     */
+    char **ptr;                             /* Désignation à convertir     */
 
     constval = (PyConstvalObject *)self;
 
@@ -146,6 +158,44 @@ static PyObject *py_constval_str(PyObject *self)
 
 /******************************************************************************
 *                                                                             *
+*  Paramètres  : self = architecture concernée par la procédure.              *
+*                args = instruction représentant le point de départ.          *
+*                                                                             *
+*  Description : Fournit une réduction de l'objet en vue d'une sérialisation. *
+*                                                                             *
+*  Retour      : Données utiles à une reconstruction.                         *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static PyObject *py_constval_reduce(PyObject *self, PyObject *args)
+{
+    PyObject *result;                       /* Données à retourner         */
+    unsigned long value;                    /* Valeur entière              */
+    PyConstvalObject *constval;             /* Autre version de l'objet    */
+    char **ptr;                             /* Désignation à convertir     */
+    PyObject *params;                       /* Paramètres de construction  */
+
+    value = PyLong_AsUnsignedLong(self);
+
+    constval = (PyConstvalObject *)self;
+
+    ptr = py_constval_compute_name_ptr(constval);
+
+    params = Py_BuildValue("(ks)", value, *ptr);
+
+    result = Py_BuildValue("(OO)", Py_TYPE(self), params);
+
+    Py_DECREF(params);
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
 *  Paramètres  : -                                                            *
 *                                                                             *
 *  Description : Fournit un accès à une définition de type à diffuser.        *
@@ -159,6 +209,11 @@ static PyObject *py_constval_str(PyObject *self)
 PyTypeObject *get_python_py_constval_type(void)
 {
     static PyMethodDef py_constval_methods[] = {
+        {
+            "__reduce__", py_constval_reduce,
+            METH_NOARGS,
+            "__reduce__($self, /)\n--\n\nProvide information to rebuild the object."
+        },
         { NULL }
     };
 
@@ -249,7 +304,7 @@ PyObject *build_constval_from_c_code(const char *name, unsigned long value)
     Py_ssize_t size;                        /* Taille supplémentaire       */
     PyTypeObject *type;                     /* Type Python 'PyConstval...' */
     PyConstvalObject *constval;             /* Autre version de l'objet    */
-    const char **ptr;                       /* Désignation à convertir     */
+    char **ptr;                             /* Désignation à convertir     */
 
     /* Création d'une base de travail */
 
@@ -293,7 +348,7 @@ PyObject *build_constval_from_c_code(const char *name, unsigned long value)
 
     ptr = py_constval_compute_name_ptr(constval);
 
-    *ptr = name;
+    *ptr = strdup(name);
 
     return result;
 
diff --git a/tests/constval.py b/tests/constval.py
index 4863d39..eafb8d3 100644
--- a/tests/constval.py
+++ b/tests/constval.py
@@ -5,18 +5,40 @@
 from chrysacase import ChrysalideTestCase
 from pychrysalide import PyConstvalObject
 from pychrysalide.arch import ArchInstruction
+import pickle
 
 
 class TestConstVal(ChrysalideTestCase):
     """TestCase for PyConstvalObject."""
 
 
-    def testGI(self):
-        """Validate the PyConstvalObject implementation."""
+    def testCreation(self):
+        """Validate PyConstvalObject creation from Python."""
+
+        cst = PyConstvalObject(123, 'XXX')
+
+        self.assertEqual(cst, 123)
+
+        self.assertEqual(str(cst), 'XXX')
 
-        with self.assertRaises(RuntimeError):
-            cv = PyConstvalObject()
+
+    def testString(self):
+        """Validate the PyConstvalObject implementation."""
 
         self.assertEqual(ArchInstruction.ILT_JUMP, 1)
 
         self.assertEqual(str(ArchInstruction.ILT_JUMP), 'ILT_JUMP')
+
+
+    def testStorage(self):
+        """Ensure PyConstvalObject instances are storable."""
+
+        cst = ArchInstruction.ILT_JUMP
+
+        data = pickle.dumps(cst)
+
+        cst = pickle.loads(data)
+
+        self.assertEqual(cst, ArchInstruction.ILT_JUMP)
+
+        self.assertEqual(str(cst), 'ILT_JUMP')
-- 
cgit v0.11.2-87-g4458