From 362ff8ddd7fac8a10c7cccae303d2ce5ea6dd7f2 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Tue, 13 Oct 2020 23:24:12 +0200
Subject: Defined proper Python bindings for basic types.

---
 plugins/pychrysalide/analysis/types/Makefile.am |   1 +
 plugins/pychrysalide/analysis/types/basic.c     |  97 +++++----------
 plugins/pychrysalide/analysis/types/constants.c | 155 ++++++++++++++++++++++++
 plugins/pychrysalide/analysis/types/constants.h |  42 +++++++
 src/analysis/types/basic.c                      |   8 +-
 src/analysis/types/basic.h                      |   2 +-
 src/analysis/types/literal.c                    |   2 +-
 tests/analysis/types/basic.py                   |  28 +++++
 8 files changed, 263 insertions(+), 72 deletions(-)
 create mode 100644 plugins/pychrysalide/analysis/types/constants.c
 create mode 100644 plugins/pychrysalide/analysis/types/constants.h
 create mode 100644 tests/analysis/types/basic.py

diff --git a/plugins/pychrysalide/analysis/types/Makefile.am b/plugins/pychrysalide/analysis/types/Makefile.am
index 53ae320..3a69345 100644
--- a/plugins/pychrysalide/analysis/types/Makefile.am
+++ b/plugins/pychrysalide/analysis/types/Makefile.am
@@ -4,6 +4,7 @@ noinst_LTLIBRARIES = libpychrysaanalysistypes.la
 libpychrysaanalysistypes_la_SOURCES =	\
 	array.h array.c						\
 	basic.h basic.c						\
+	constants.h constants.c				\
 	cse.h cse.c							\
 	encaps.h encaps.c					\
 	expr.h expr.c						\
diff --git a/plugins/pychrysalide/analysis/types/basic.c b/plugins/pychrysalide/analysis/types/basic.c
index e421389..539bcad 100644
--- a/plugins/pychrysalide/analysis/types/basic.c
+++ b/plugins/pychrysalide/analysis/types/basic.c
@@ -32,6 +32,7 @@
 #include <analysis/types/basic.h>
 
 
+#include "constants.h"
 #include "../type.h"
 #include "../../access.h"
 #include "../../helpers.h"
@@ -42,10 +43,7 @@
 static PyObject *py_basic_type_new(PyTypeObject *, PyObject *, PyObject *);
 
 /* Fournit le type de base géré par le type. */
-static PyObject *py_basic_type_get_base_type(PyObject *, void *);
-
-/* Définit les constantes pour les types de base. */
-static bool py_basic_type_define_constants(PyTypeObject *);
+static PyObject *py_basic_type_get_base(PyObject *, void *);
 
 
 
@@ -66,11 +64,22 @@ static bool py_basic_type_define_constants(PyTypeObject *);
 static PyObject *py_basic_type_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 {
     PyObject *result;                       /* Instance à retourner        */
-    unsigned long base;                     /* Type de base à créer        */
+    BaseType base;                          /* Type de base à créer        */
     int ret;                                /* Bilan de lecture des args.  */
     GDataType *dtype;                       /* Version GLib du type        */
 
-    ret = PyArg_ParseTuple(args, "k", &base);
+#define BASIC_TYPE_DOC                                                          \
+    "The BasicType class handles all the primary types of data, such as"        \
+    " integers, characters or floating numbers.\n"                              \
+    "\n"                                                                        \
+    "Instances can be created using the following constructor:\n"               \
+    "\n"                                                                        \
+    "    BasicType(base)"                                               \
+    "\n"                                                                        \
+    "Where *base* is one of the pychrysalide.analysis.types.BasicType.BaseType" \
+    " values, except *BaseType.INVALID*."
+
+    ret = PyArg_ParseTuple(args, "O&", convert_to_basic_type_base_type, &base);
     if (!ret) return NULL;
 
     if (base >= BTP_INVALID)
@@ -101,17 +110,25 @@ static PyObject *py_basic_type_new(PyTypeObject *type, PyObject *args, PyObject
 *                                                                             *
 ******************************************************************************/
 
-static PyObject *py_basic_type_get_base_type(PyObject *self, void *closure)
+static PyObject *py_basic_type_get_base(PyObject *self, void *closure)
 {
     PyObject *result;                       /* Résultat à retourner        */
     GBasicType *type;                       /* Version GLib du type        */
     BaseType base;                          /* Type de base à renvoyer     */
 
+#define BASIC_TYPE_BASE_ATTRIB PYTHON_GET_DEF_FULL                      \
+(                                                                       \
+    base, py_basic_type,                                                \
+    "Provide the internal identifier of the basic type.\n"              \
+    "\n"                                                                \
+    "This property provides a pychrysalide.analysis.BaseType value."    \
+)
+
     type = G_BASIC_TYPE(pygobject_get(self));
 
-    base = g_basic_type_get_base_type(type);
+    base = g_basic_type_get_base(type);
 
-    result = PyLong_FromUnsignedLong(base);
+    result = cast_with_constants_group_from_type(get_python_basic_type_type(), "BaseType", base);
 
     return result;
 
@@ -137,10 +154,7 @@ PyTypeObject *get_python_basic_type_type(void)
     };
 
     static PyGetSetDef py_basic_type_getseters[] = {
-        {
-            "base", py_basic_type_get_base_type, NULL,
-            "Provide the internal identifier of the basic type.", NULL
-        },
+        BASIC_TYPE_BASE_ATTRIB,
         { NULL }
     };
 
@@ -153,7 +167,7 @@ PyTypeObject *get_python_basic_type_type(void)
 
         .tp_flags       = Py_TPFLAGS_DEFAULT,
 
-        .tp_doc         = "PyChrysalide basic type",
+        .tp_doc         = BASIC_TYPE_DOC,
 
         .tp_methods     = py_basic_type_methods,
         .tp_getset      = py_basic_type_getseters,
@@ -168,59 +182,6 @@ PyTypeObject *get_python_basic_type_type(void)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : obj_type = type dont le dictionnaire est à compléter.        *
-*                                                                             *
-*  Description : Définit les constantes pour les types de base.               *
-*                                                                             *
-*  Retour      : -                                                            *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
-
-static bool py_basic_type_define_constants(PyTypeObject *obj_type)
-{
-    bool result;                            /* Bilan à retourner           */
-
-    result = true;
-
-    result &= PyDict_AddULongMacro(obj_type, BTP_VOID);
-    result &= PyDict_AddULongMacro(obj_type, BTP_WCHAR_T);
-    result &= PyDict_AddULongMacro(obj_type, BTP_BOOL);
-    result &= PyDict_AddULongMacro(obj_type, BTP_CHAR);
-    result &= PyDict_AddULongMacro(obj_type, BTP_SCHAR);
-    result &= PyDict_AddULongMacro(obj_type, BTP_UCHAR);
-    result &= PyDict_AddULongMacro(obj_type, BTP_SHORT);
-    result &= PyDict_AddULongMacro(obj_type, BTP_USHORT);
-    result &= PyDict_AddULongMacro(obj_type, BTP_INT);
-    result &= PyDict_AddULongMacro(obj_type, BTP_UINT);
-    result &= PyDict_AddULongMacro(obj_type, BTP_LONG);
-    result &= PyDict_AddULongMacro(obj_type, BTP_ULONG);
-    result &= PyDict_AddULongMacro(obj_type, BTP_LONG_LONG);
-    result &= PyDict_AddULongMacro(obj_type, BTP_ULONG_LONG);
-    result &= PyDict_AddULongMacro(obj_type, BTP_INT128);
-    result &= PyDict_AddULongMacro(obj_type, BTP_UINT128);
-    result &= PyDict_AddULongMacro(obj_type, BTP_FLOAT);
-    result &= PyDict_AddULongMacro(obj_type, BTP_DOUBLE);
-    result &= PyDict_AddULongMacro(obj_type, BTP_LONG_DOUBLE);
-    result &= PyDict_AddULongMacro(obj_type, BTP_FLOAT128);
-    result &= PyDict_AddULongMacro(obj_type, BTP_ELLIPSIS);
-    result &= PyDict_AddULongMacro(obj_type, BTP_754R_64);
-    result &= PyDict_AddULongMacro(obj_type, BTP_754R_128);
-    result &= PyDict_AddULongMacro(obj_type, BTP_754R_32);
-    result &= PyDict_AddULongMacro(obj_type, BTP_754R_16);
-    result &= PyDict_AddULongMacro(obj_type, BTP_CHAR32_T);
-    result &= PyDict_AddULongMacro(obj_type, BTP_CHAR16_T);
-
-    result &= PyDict_AddULongMacro(obj_type, BTP_INVALID);
-
-    return result;
-
-}
-
-
-/******************************************************************************
-*                                                                             *
 *  Paramètres  : module = module dont la définition est à compléter.          *
 *                                                                             *
 *  Description : Prend en charge l'objet 'pychrysalide.....types.BasicType'.  *
@@ -251,7 +212,7 @@ bool ensure_python_basic_type_is_registered(void)
         if (!register_class_for_pygobject(dict, G_TYPE_BASIC_TYPE, type, get_python_data_type_type()))
             return false;
 
-        if (!py_basic_type_define_constants(type))
+        if (!define_basic_type_constants(type))
             return false;
 
     }
diff --git a/plugins/pychrysalide/analysis/types/constants.c b/plugins/pychrysalide/analysis/types/constants.c
new file mode 100644
index 0000000..6562fa0
--- /dev/null
+++ b/plugins/pychrysalide/analysis/types/constants.c
@@ -0,0 +1,155 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * constants.c - ajout des constantes de base pour les types
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "constants.h"
+
+
+#include <analysis/types/basic.h>
+
+
+#include "../../helpers.h"
+
+
+
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : type = type dont le dictionnaire est à compléter.            *
+*                                                                             *
+*  Description : Définit les constantes relatives aux types de base.          *
+*                                                                             *
+*  Retour      : true en cas de succès de l'opération, false sinon.           *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+bool define_basic_type_constants(PyTypeObject *type)
+{
+    bool result;                            /* Bilan à retourner           */
+    PyObject *values;                       /* Groupe de valeurs à établir */
+
+    values = PyDict_New();
+
+    result = add_const_to_group(values, "VOID", BTP_VOID);
+    if (result) result = add_const_to_group(values, "WCHAR_T", BTP_WCHAR_T);
+    if (result) result = add_const_to_group(values, "BOOL", BTP_BOOL);
+    if (result) result = add_const_to_group(values, "CHAR", BTP_CHAR);
+    if (result) result = add_const_to_group(values, "SCHAR", BTP_SCHAR);
+    if (result) result = add_const_to_group(values, "UCHAR", BTP_UCHAR);
+    if (result) result = add_const_to_group(values, "SHORT", BTP_SHORT);
+    if (result) result = add_const_to_group(values, "USHORT", BTP_USHORT);
+    if (result) result = add_const_to_group(values, "INT", BTP_INT);
+    if (result) result = add_const_to_group(values, "UINT", BTP_UINT);
+    if (result) result = add_const_to_group(values, "LONG", BTP_LONG);
+    if (result) result = add_const_to_group(values, "ULONG", BTP_ULONG);
+    if (result) result = add_const_to_group(values, "LONG_LONG", BTP_LONG_LONG);
+    if (result) result = add_const_to_group(values, "ULONG_LONG", BTP_ULONG_LONG);
+    if (result) result = add_const_to_group(values, "INT128", BTP_INT128);
+    if (result) result = add_const_to_group(values, "UINT128", BTP_UINT128);
+    if (result) result = add_const_to_group(values, "FLOAT", BTP_FLOAT);
+    if (result) result = add_const_to_group(values, "DOUBLE", BTP_DOUBLE);
+    if (result) result = add_const_to_group(values, "LONG_DOUBLE", BTP_LONG_DOUBLE);
+    if (result) result = add_const_to_group(values, "FLOAT128", BTP_FLOAT128);
+    if (result) result = add_const_to_group(values, "ELLIPSIS", BTP_ELLIPSIS);
+    if (result) result = add_const_to_group(values, "754R_64", BTP_754R_64);
+    if (result) result = add_const_to_group(values, "754R_128", BTP_754R_128);
+    if (result) result = add_const_to_group(values, "754R_32", BTP_754R_32);
+    if (result) result = add_const_to_group(values, "754R_16", BTP_754R_16);
+    if (result) result = add_const_to_group(values, "754R_N", BTP_754R_N);
+    if (result) result = add_const_to_group(values, "CHAR32_T", BTP_CHAR32_T);
+    if (result) result = add_const_to_group(values, "CHAR16_T", BTP_CHAR16_T);
+    if (result) result = add_const_to_group(values, "AUTO", BTP_AUTO);
+    if (result) result = add_const_to_group(values, "DECL_AUTO", BTP_DECL_AUTO);
+    if (result) result = add_const_to_group(values, "INVALID", BTP_INVALID);
+
+    if (!result)
+    {
+        Py_DECREF(values);
+        goto exit;
+    }
+
+    result = attach_constants_group_to_type(type, false, "BaseType", values,
+                                            "Identifiers for basic data types.");
+
+ exit:
+
+    return result;
+
+}
+
+
+/******************************************************************************
+*                                                                             *
+*  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 constante BaseType.                    *
+*                                                                             *
+*  Retour      : Bilan de l'opération, voire indications supplémentaires.     *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+int convert_to_basic_type_base_type(PyObject *arg, void *dst)
+{
+    int result;                             /* Bilan à retourner           */
+    unsigned long value;                    /* Valeur transcrite           */
+
+    result = PyObject_IsInstance(arg, (PyObject *)&PyLong_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 BaseType");
+            break;
+
+        case 1:
+            value = PyLong_AsUnsignedLong(arg);
+
+            if (value > BTP_INVALID)
+            {
+                PyErr_SetString(PyExc_TypeError, "invalid value for BaseType");
+                result = 0;
+            }
+
+            else
+                *((BaseType *)dst) = value;
+
+            break;
+
+        default:
+            assert(false);
+            break;
+
+    }
+
+    return result;
+
+}
diff --git a/plugins/pychrysalide/analysis/types/constants.h b/plugins/pychrysalide/analysis/types/constants.h
new file mode 100644
index 0000000..9598f75
--- /dev/null
+++ b/plugins/pychrysalide/analysis/types/constants.h
@@ -0,0 +1,42 @@
+
+/* Chrysalide - Outil d'analyse de fichiers binaires
+ * constants.h - prototypes pour l'ajout des constantes de base pour les types
+ *
+ * Copyright (C) 2020 Cyrille Bagard
+ *
+ *  This file is part of Chrysalide.
+ *
+ *  Chrysalide is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  Chrysalide is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#ifndef _PLUGINS_PYCHRYSALIDE_ANALYSIS_TYPES_CONSTANTS_H
+#define _PLUGINS_PYCHRYSALIDE_ANALYSIS_TYPES_CONSTANTS_H
+
+
+#include <Python.h>
+#include <stdbool.h>
+
+
+
+/* Définit les constantes relatives aux types de base. */
+bool define_basic_type_constants(PyTypeObject *);
+
+/* Tente de convertir en constante BaseType. */
+int convert_to_basic_type_base_type(PyObject *, void *);
+
+
+
+#endif  /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_TYPES_CONSTANTS_H */
diff --git a/src/analysis/types/basic.c b/src/analysis/types/basic.c
index 755a6f6..a907f2d 100644
--- a/src/analysis/types/basic.c
+++ b/src/analysis/types/basic.c
@@ -371,8 +371,12 @@ static char *g_basic_type_to_string(const GBasicType *type, bool include)
 *                                                                             *
 ******************************************************************************/
 
-BaseType g_basic_type_get_base_type(const GBasicType *type)
+BaseType g_basic_type_get_base(const GBasicType *type)
 {
-    return type->type;
+    BaseType result;                        /* Type de base à retourner    */
+
+    result = type->type;
+
+    return result;
 
 }
diff --git a/src/analysis/types/basic.h b/src/analysis/types/basic.h
index d2b3055..ad7308f 100644
--- a/src/analysis/types/basic.h
+++ b/src/analysis/types/basic.h
@@ -93,7 +93,7 @@ GType g_basic_type_get_type(void);
 GDataType *g_basic_type_new(BaseType);
 
 /* Fournit le type de base géré par le type. */
-BaseType g_basic_type_get_base_type(const GBasicType *);
+BaseType g_basic_type_get_base(const GBasicType *);
 
 
 
diff --git a/src/analysis/types/literal.c b/src/analysis/types/literal.c
index 831fcb7..3a9e5cf 100644
--- a/src/analysis/types/literal.c
+++ b/src/analysis/types/literal.c
@@ -243,7 +243,7 @@ static char *g_literal_type_to_string(const GLiteralType *type, bool include)
 {
     char *result;                           /* Valeur à renvoyer           */
 
-    switch (g_basic_type_get_base_type(G_BASIC_TYPE(type->orig)))
+    switch (g_basic_type_get_base(G_BASIC_TYPE(type->orig)))
     {
         case BTP_BOOL:
             result = strdup(type->value.int_val ? "true" : "false");
diff --git a/tests/analysis/types/basic.py b/tests/analysis/types/basic.py
new file mode 100644
index 0000000..7b7403b
--- /dev/null
+++ b/tests/analysis/types/basic.py
@@ -0,0 +1,28 @@
+#!/usr/bin/python3-dbg
+# -*- coding: utf-8 -*-
+
+
+from chrysacase import ChrysalideTestCase
+from pychrysalide.analysis.types import BasicType
+
+
+class TestDataType(ChrysalideTestCase):
+    """TestCase for analysis.DataType."""
+
+
+    def testBasicTypeConstructor(self):
+        """Build some basic types."""
+
+        tp = BasicType(BasicType.BaseType.VOID)
+
+        self.assertEqual(str(tp), 'void')
+
+        self.assertEqual(tp.base, BasicType.BaseType.VOID)
+
+        with self.assertRaisesRegex(TypeError, 'Bad basic type.'):
+
+            tp = BasicType(BasicType.BaseType.INVALID)
+
+        with self.assertRaisesRegex(TypeError, 'invalid value for BaseType'):
+
+            tp = BasicType(0x1234)
-- 
cgit v0.11.2-87-g4458