From 0b0f69bd1278b8f5d95c6ea8fb56915148992a77 Mon Sep 17 00:00:00 2001 From: Cyrille Bagard Date: Sat, 10 Oct 2020 14:01:40 +0200 Subject: Updated the basic types definition and its Python bindings. --- plugins/pychrysalide/analysis/constants.c | 98 +++++ plugins/pychrysalide/analysis/constants.h | 6 + plugins/pychrysalide/analysis/type.c | 707 +++++++++++++++++++++++++++--- plugins/pychrysalide/analysis/type.h | 3 + plugins/pychrysalide/helpers.c | 50 +++ plugins/pychrysalide/helpers.h | 22 + src/analysis/type-int.h | 6 +- src/analysis/type.c | 33 +- src/analysis/type.h | 3 + src/analysis/types/proto.c | 30 +- tests/analysis/type.py | 92 ++++ 11 files changed, 973 insertions(+), 77 deletions(-) create mode 100644 tests/analysis/type.py diff --git a/plugins/pychrysalide/analysis/constants.c b/plugins/pychrysalide/analysis/constants.c index dc507dc..8d68edc 100644 --- a/plugins/pychrysalide/analysis/constants.c +++ b/plugins/pychrysalide/analysis/constants.c @@ -25,6 +25,7 @@ #include "constants.h" +#include #include @@ -212,3 +213,100 @@ int convert_to_memory_data_size(PyObject *arg, void *dst) return result; } + + +/****************************************************************************** +* * +* Paramètres : type = type dont le dictionnaire est à compléter. * +* * +* Description : Définit les constantes pour les types de données. * +* * +* Retour : true en cas de succès de l'opération, false sinon. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool define_analysis_data_type_constants(PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + PyObject *values; /* Groupe de valeurs à établir */ + + values = PyDict_New(); + + result = add_const_to_group(values, "NONE", TQF_NONE); + if (result) result = add_const_to_group(values, "RESTRICT", TQF_RESTRICT); + if (result) result = add_const_to_group(values, "VOLATILE", TQF_VOLATILE); + if (result) result = add_const_to_group(values, "CONST", TQF_CONST); + if (result) result = add_const_to_group(values, "ALL", TQF_ALL); + + if (!result) + { + Py_DECREF(values); + goto exit; + } + + result = attach_constants_group_to_type(type, false, "TypeQualifier", values, + "Qualifier for a data type."); + + 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 TypeQualifier. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_data_type_qualifier(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 TypeQualifier"); + break; + + case 1: + value = PyLong_AsUnsignedLong(arg); + + if ((value & ~TQF_ALL) != 0) + { + PyErr_SetString(PyExc_TypeError, "invalid value for TypeQualifier"); + result = 0; + } + + else + *((TypeQualifier *)dst) = value; + + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/analysis/constants.h b/plugins/pychrysalide/analysis/constants.h index 45982bb..fb96e99 100644 --- a/plugins/pychrysalide/analysis/constants.h +++ b/plugins/pychrysalide/analysis/constants.h @@ -39,6 +39,12 @@ int convert_to_source_endian(PyObject *, void *); /* Tente de convertir en constante MemoryDataSize. */ int convert_to_memory_data_size(PyObject *, void *); +/* Définit les constantes pour les types de données. */ +bool define_analysis_data_type_constants(PyTypeObject *); + +/* Tente de convertir en constante TypeQualifier. */ +int convert_to_data_type_qualifier(PyObject *, void *); + #endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_CONSTANTS_H */ diff --git a/plugins/pychrysalide/analysis/type.c b/plugins/pychrysalide/analysis/type.c index 6043042..cd849e4 100644 --- a/plugins/pychrysalide/analysis/type.c +++ b/plugins/pychrysalide/analysis/type.c @@ -33,17 +33,54 @@ #include -#include +#include +#include +#include "constants.h" #include "../access.h" #include "../helpers.h" +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Accompagne la création d'une instance dérivée en Python. */ +static PyObject *py_data_type_new(PyTypeObject *, PyObject *, PyObject *); + +/* Initialise la classe des types quelconques. */ +static void py_data_type_init_gclass(GDataTypeClass *, gpointer); + +/* Crée un copie d'un type existant. */ +static GDataType *py_data_type_dup_wrapper(const GDataType *); + +/* Décrit le type fourni sous forme de caractères. */ +static char *py_data_type_to_string_wrapper(const GDataType *, bool); + +/* Indique si le type assure une gestion des espaces de noms. */ +static bool py_data_type_handle_namespaces_wrapper(const GDataType *); + +/* Indique si le type est un pointeur. */ +static bool py_data_type_is_pointer_wrapper(const GDataType *); + +/* Indique si le type est une référence. */ +static bool py_data_type_is_reference_wrapper(const GDataType *); + +/* Initialise une instance sur la base du dérivé de GObject. */ +static int py_data_type_init(PyObject *, PyObject *, PyObject *); + + + +/* ----------------- FONCTIONNALITES BASIQUES POUR TYPES DE DONNEES ----------------- */ + + /* Décrit le type fourni sous forme de caractères. */ static PyObject *py_data_type_to_str(PyObject *); +/* Crée un copie d'un type existant. */ +static PyObject *py_data_type_dup(PyObject *, PyObject *); + /* Fournit le groupe d'appartenance d'un type donné. */ static PyObject *py_data_type_get_namespace(PyObject *, void *); @@ -56,16 +93,432 @@ static PyObject *py_data_type_get_qualifiers(PyObject *, void *); /* Définit l'ensemble des qualificatifs d'une instance de type. */ static int py_data_type_set_qualifiers(PyObject *, PyObject *, void *); +/* Indique si le type assure une gestion des espaces de noms. */ +static PyObject *py_data_type_handle_namespaces(PyObject *, void *); + /* Indique si le type est un pointeur. */ static PyObject *py_data_type_is_pointer(PyObject *, void *); /* Indique si le type est une référence. */ static PyObject *py_data_type_is_reference(PyObject *, void *); -/* Définit les constantes pour les types de données. */ -static bool py_data_type_define_constants(PyTypeObject *); +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : type = type du nouvel objet à mettre en place. * +* args = éventuelle liste d'arguments. * +* kwds = éventuel dictionnaire de valeurs mises à disposition. * +* * +* Description : Accompagne la création d'une instance dérivée en Python. * +* * +* Retour : Nouvel objet Python mis en place ou NULL en cas d'échec. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_data_type_new(PyTypeObject *type, PyObject *args, PyObject *kwds) +{ + 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 */ + + /* Validations diverses */ + + base = get_python_data_type_type(); + + if (type == base) + { + result = NULL; + PyErr_Format(PyExc_RuntimeError, _("%s is an abstract class"), type->tp_name); + goto exit; + } + + /* Mise en place d'un type dédié */ + + first_time = (g_type_from_name(type->tp_name) == 0); + + gtype = build_dynamic_type(G_TYPE_DATA_TYPE, type->tp_name, + (GClassInitFunc)py_data_type_init_gclass, NULL, NULL); + + if (first_time) + { + status = register_class_for_dynamic_pygobject(gtype, type, base); + + if (!status) + { + result = NULL; + goto exit; + } + + } + + /* On crée, et on laisse ensuite la main à PyGObject_Type.tp_init() */ + + result = PyType_GenericNew(type, args, kwds); + + exit: + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : class = classe à initialiser. * +* unused = données non utilisées ici. * +* * +* Description : Initialise la classe des types quelconques. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_data_type_init_gclass(GDataTypeClass *class, gpointer unused) +{ + class->dup = py_data_type_dup_wrapper; + class->to_string = py_data_type_to_string_wrapper; + + class->handle_ns = py_data_type_handle_namespaces_wrapper; + class->is_pointer = py_data_type_is_pointer_wrapper; + class->is_reference = py_data_type_is_reference_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type à dupliquer. * +* * +* Description : Crée un copie d'un type existant. * +* * +* Retour : Nouvelle instance de type identique à celle fournie. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static GDataType *py_data_type_dup_wrapper(const GDataType *type) +{ + GDataType *result; /* Copie à retourner */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define DATA_TYPE_DUP_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _dup, "$self, /", \ + METH_NOARGS, \ + "Abstract method used to create a copy of a data type.\n" \ + "\n" \ + "The returned value has to be a new instance of the" \ + " pychrysalide.analysis.DataType class." \ +) + + result = NULL; + + pyobj = pygobject_new(G_OBJECT(type)); + + if (has_python_method(pyobj, "_dup")) + { + pyret = run_python_method(pyobj, "_dup", NULL); + + if (pyret != NULL) + { + if (PyObject_TypeCheck(pyret, get_python_data_type_type())) + { + result = G_DATA_TYPE(pygobject_get(pyret)); + g_object_ref(G_OBJECT(result)); + } + } + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type à convertir. * +* include = doit-on inclure les espaces de noms ? * +* * +* Description : Décrit le type fourni sous forme de caractères. * +* * +* Retour : Chaîne à libérer de la mémoire après usage. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static char *py_data_type_to_string_wrapper(const GDataType *type, bool include) +{ + char *result; /* Etiquette à retourner */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *arg; /* Version Python de l'argument*/ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define DATA_TYPE_TO_STRING_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _to_string, "$self, include, /", \ + METH_VARARGS, \ + "Abstract method used to provide the string represention of" \ + " a data type.\n" \ + "\n" \ + "The *include* argument defines if the type namespace has to" \ + " get prepended, if it exists.\n" \ + "\n" \ + "The returned value has to be a string." \ +) + + result = NULL; + + pyobj = pygobject_new(G_OBJECT(type)); + + if (has_python_method(pyobj, "_to_string")) + { + arg = include ? Py_True : Py_False; + Py_INCREF(arg); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, arg); + + pyret = run_python_method(pyobj, "_to_string", args); + + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) + result = strdup(PyUnicode_DATA(pyret)); + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + } + + Py_DECREF(pyobj); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type à consulter. * +* * +* Description : Indique si le type assure une gestion des espaces de noms. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_data_type_handle_namespaces_wrapper(const GDataType *type) +{ + bool result; /* Bilan à retourner */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define DATA_TYPE_HANDLE_NAMESPACES_WRAPPER PYTHON_TRUE_WRAPPER_DEF \ +( \ + _handle_namespaces, "$self, /", \ + METH_NOARGS, \ + "Abstract method used to state if the type handles namespaces" \ + " or not.\n" \ + "\n" \ + "The return is a boolean value. If this method does not" \ + " exist, the True value is assumed." \ +) + + result = true; + + pyobj = pygobject_new(G_OBJECT(type)); + + if (has_python_method(pyobj, "_handle_namespaces")) + { + pyret = run_python_method(pyobj, "_handle_namespaces", NULL); + + result = (pyret == Py_True); + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type à consulter. * +* * +* Description : Indique si le type est un pointeur. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_data_type_is_pointer_wrapper(const GDataType *type) +{ + bool result; /* Bilan à retourner */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define DATA_TYPE_IS_POINTER_WRAPPER PYTHON_FALSE_WRAPPER_DEF \ +( \ + _is_pointer, "$self, /", \ + METH_NOARGS, \ + "Abstract method used to state if the type points to" \ + " another type or not.\n" \ + "\n" \ + "The return is a boolean value. If this method does not" \ + " exist, the False value is assumed." \ +) + + result = false; + + pyobj = pygobject_new(G_OBJECT(type)); + + if (has_python_method(pyobj, "_is_pointer")) + { + pyret = run_python_method(pyobj, "_is_pointer", NULL); + + result = (pyret == Py_True); + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type à consulter. * +* * +* Description : Indique si le type est une référence. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool py_data_type_is_reference_wrapper(const GDataType *type) +{ + bool result; /* Bilan à retourner */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + +#define DATA_TYPE_IS_REFERENCE_WRAPPER PYTHON_FALSE_WRAPPER_DEF \ +( \ + _is_reference, "$self, /", \ + METH_NOARGS, \ + "Abstract method used to state if the type refers to" \ + " another type or not.\n" \ + "\n" \ + "The return is a boolean value. If this method does not" \ + " exist, the False value is assumed." \ +) + + result = false; + + pyobj = pygobject_new(G_OBJECT(type)); + + if (has_python_method(pyobj, "_is_reference")) + { + pyret = run_python_method(pyobj, "_is_reference", NULL); + + result = (pyret == Py_True); + + Py_XDECREF(pyret); + + } + + Py_DECREF(pyobj); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Initialise une instance sur la base du dérivé de GObject. * +* * +* Retour : 0. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_data_type_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int ret; /* Bilan de lecture des args. */ + +#define DATA_TYPE_DOC \ + "The DataType object is the base class for all data types.\n" \ + "\n" \ + "Instances can be created using the following constructor:\n" \ + "\n" \ + " DataType()" \ + "\n" \ + "The following methods have to be defined for new classes:\n" \ + "* pychrysalide.analysis.DataType._dup();\n" \ + "* pychrysalide.analysis.DataType._to_string()." \ + "\n" \ + "Some extra method definitions are optional for new classes:\n" \ + "* pychrysalide.analysis.DataType._handle_namespaces();\n" \ + "* pychrysalide.analysis.DataType._is_pointer();\n" \ + "* pychrysalide.analysis.DataType._is_reference();" \ + + /* Initialisation d'un objet GLib */ + + ret = forward_pygobjet_init(self); + if (ret == -1) return -1; + + return 0; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* FONCTIONNALITES BASIQUES POUR TYPES DE DONNEES */ +/* ---------------------------------------------------------------------------------- */ + /****************************************************************************** * * @@ -100,6 +553,56 @@ static PyObject *py_data_type_to_str(PyObject *self) /****************************************************************************** * * +* Paramètres : self = serveur à manipuler. * +* args = arguments d'appel non utilisés ici. * +* * +* Description : Crée un copie d'un type existant. * +* * +* Retour : Nouvelle instance de type identique à celle fournie. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_data_type_dup(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + GDataType *type; /* Version native de l'objet */ + GDataType *copy; /* Copie du type obtenue */ + +#define DATA_TYPE_DUP_METHOD PYTHON_METHOD_DEF \ +( \ + dup, "$self, /", \ + METH_NOARGS, py_data_type, \ + "Create a copy of a data type.\n" \ + "\n" \ + "The returned value has to be a new instance of the" \ + " pychrysalide.analysis.DataType class." \ +) + + type = G_DATA_TYPE(pygobject_get(self)); + + copy = g_data_type_dup(type); + + if (copy != NULL) + { + result = pygobject_new(G_OBJECT(copy)); + g_object_unref(G_OBJECT(copy)); + } + + else + { + result = Py_None; + Py_INCREF(result); + } + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * @@ -116,14 +619,33 @@ static PyObject *py_data_type_get_namespace(PyObject *self, void *closure) PyObject *result; /* Valeur à retourner */ GDataType *type; /* Elément à consulter */ GDataType *ns; /* Espace de noms */ + const char *sep; /* Séparateur d'espace */ + +#define DATA_TYPE_NAMESPACE_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + namespace, py_data_type, \ + "Namespace for the type, None if any.\n" \ + "\n" \ + "This property carries a tuple of two values:\n" \ + "* a namespace, as a pychrysalide.analysis.DataType.TypeQualifier" \ + " instance;\n" \ + "* a namespace separator, as a string." \ +) type = G_DATA_TYPE(pygobject_get(self)); + ns = g_data_type_get_namespace(type); + sep = g_data_type_get_namespace_separator(type); - if (ns != NULL) + if (ns != NULL && sep != NULL) { - result = pygobject_new(G_OBJECT(ns)); + result = PyTuple_New(2); + + PyTuple_SetItem(result, 0, pygobject_new(G_OBJECT(ns))); g_object_unref(G_OBJECT(ns)); + + PyTuple_SetItem(result, 1, PyUnicode_FromString(sep)); + } else @@ -184,7 +706,7 @@ static int py_data_type_set_namespace(PyObject *self, PyObject *value, void *clo } ns = G_DATA_TYPE(pygobject_get(PyTuple_GetItem(value, 0))); - sep = strdup(PyUnicode_DATA(pygobject_get(PyTuple_GetItem(value, 1)))); + sep = strdup(PyUnicode_DATA(PyTuple_GetItem(value, 1))); g_object_ref(G_OBJECT(ns)); g_data_type_set_namespace(type, ns, sep); @@ -215,10 +737,18 @@ static PyObject *py_data_type_get_qualifiers(PyObject *self, void *closure) GDataType *type; /* Elément à consulter */ TypeQualifier qualifiers; /* Qualificatifs en place */ +#define DATA_TYPE_QUALIFIERS_ATTRIB PYTHON_GETSET_DEF_FULL \ +( \ + qualifiers, py_data_type, \ + "Qualifier for the data type, *TypeQualifier.NONE* if any.\n" \ + "\n" \ + "This property carries a pychrysalide.analysis.DataType.TypeQualifier value." \ +) + type = G_DATA_TYPE(pygobject_get(self)); qualifiers = g_data_type_get_qualifiers(type); - result = PyLong_FromUnsignedLong(qualifiers); + result = cast_with_constants_group_from_type(get_python_data_type_type(), "TypeQualifier", qualifiers); return result; @@ -242,21 +772,11 @@ static PyObject *py_data_type_get_qualifiers(PyObject *self, void *closure) static int py_data_type_set_qualifiers(PyObject *self, PyObject *value, void *closure) { GDataType *type; /* Elément à traiter */ - unsigned long qualifiers; /* Qualificatifs à intégrer */ - - if (!PyLong_Check(value)) - { - PyErr_SetString(PyExc_TypeError, _("The attribute value must be a integer mask.")); - return -1; - } - - qualifiers = PyLong_AsUnsignedLong(value); + TypeQualifier qualifiers; /* Qualificatifs à intégrer */ + int ret; /* Bilan d'une conversion */ - if (qualifiers > TQF_ALL) - { - PyErr_SetString(PyExc_TypeError, _("Invalid value for type qualifiers.")); - return -1; - } + ret = convert_to_data_type_qualifier(value, &qualifiers); + if (ret != 1) return -1; type = G_DATA_TYPE(pygobject_get(self)); @@ -272,6 +792,43 @@ static int py_data_type_set_qualifiers(PyObject *self, PyObject *value, void *cl * Paramètres : self = objet Python concerné par l'appel. * * closure = non utilisé ici. * * * +* Description : Indique si le type assure une gestion des espaces de noms. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_data_type_handle_namespaces(PyObject *self, void *closure) +{ + PyObject *result; /* Valeur à retourner */ + GDataType *type; /* Elément à consulter */ + bool status; /* Etat à faire suivre */ + +#define DATA_TYPE_NAMESPACES_ATTRIB PYTHON_RAWGET_DEF_FULL \ +( \ + handle_namespaces, py_data_type, \ + "True if the type handles namespaces, False otherwise." \ +) + + type = G_DATA_TYPE(pygobject_get(self)); + + status = g_data_type_handle_namespaces(type); + + result = status ? Py_True : Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * +* * * Description : Indique si le type est un pointeur. * * * * Retour : Bilan de la consultation. * @@ -286,6 +843,12 @@ static PyObject *py_data_type_is_pointer(PyObject *self, void *closure) GDataType *type; /* Elément à consulter */ bool status; /* Etat à faire suivre */ +#define DATA_TYPE_POINTER_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + pointer, py_data_type, \ + "True if the type is a pointer, False otherwise." \ +) + type = G_DATA_TYPE(pygobject_get(self)); status = g_data_type_is_pointer(type); @@ -317,6 +880,12 @@ static PyObject *py_data_type_is_reference(PyObject *self, void *closure) GDataType *type; /* Elément à consulter */ bool status; /* Etat à faire suivre */ +#define DATA_TYPE_REFERENCE_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + reference, py_data_type, \ + "True if the type is a reference, False otherwise." \ +) + type = G_DATA_TYPE(pygobject_get(self)); status = g_data_type_is_reference(type); @@ -344,26 +913,20 @@ static PyObject *py_data_type_is_reference(PyObject *self, void *closure) PyTypeObject *get_python_data_type_type(void) { static PyMethodDef py_data_type_methods[] = { + DATA_TYPE_DUP_WRAPPER, + DATA_TYPE_TO_STRING_WRAPPER, + DATA_TYPE_HANDLE_NAMESPACES_WRAPPER, + DATA_TYPE_IS_POINTER_WRAPPER, + DATA_TYPE_DUP_METHOD, { NULL } }; static PyGetSetDef py_data_type_getseters[] = { - { - "namespace", py_data_type_get_namespace, py_data_type_set_namespace, - "Namespace for the type, None if any.", NULL - }, - { - "qualifiers", py_data_type_get_qualifiers, py_data_type_set_qualifiers, - "Qualifiers linked to the type, TQF_NONE if any.", NULL - }, - { - "is_pointer", py_data_type_is_pointer, NULL, - "True if the type is a pointer.", NULL - }, - { - "is_reference", py_data_type_is_reference, NULL, - "True if the type is a reference.", NULL - }, + DATA_TYPE_NAMESPACE_ATTRIB, + DATA_TYPE_QUALIFIERS_ATTRIB, + DATA_TYPE_NAMESPACES_ATTRIB, + DATA_TYPE_POINTER_ATTRIB, + DATA_TYPE_REFERENCE_ATTRIB, { NULL } }; @@ -377,10 +940,13 @@ PyTypeObject *get_python_data_type_type(void) .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_doc = "PyChrysalide data type", + .tp_doc = DATA_TYPE_DOC, .tp_methods = py_data_type_methods, - .tp_getset = py_data_type_getseters + .tp_getset = py_data_type_getseters, + + .tp_init = py_data_type_init, + .tp_new = py_data_type_new }; @@ -391,36 +957,6 @@ PyTypeObject *get_python_data_type_type(void) /****************************************************************************** * * -* Paramètres : obj_type = type dont le dictionnaire est à compléter. * -* * -* Description : Définit les constantes pour les types de données. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static bool py_data_type_define_constants(PyTypeObject *obj_type) -{ - bool result; /* Bilan à retourner */ - - result = true; - - result &= PyDict_AddULongMacro(obj_type, TQF_NONE); - result &= PyDict_AddULongMacro(obj_type, TQF_RESTRICT); - result &= PyDict_AddULongMacro(obj_type, TQF_VOLATILE); - result &= PyDict_AddULongMacro(obj_type, TQF_CONST); - - result &= PyDict_AddULongMacro(obj_type, TQF_ALL); - - return result; - -} - - -/****************************************************************************** -* * * Paramètres : - * * * * Description : Prend en charge l'objet 'pychrysalide.analysis.DataType'. * @@ -448,7 +984,7 @@ bool ensure_python_data_type_is_registered(void) if (!register_class_for_pygobject(dict, G_TYPE_DATA_TYPE, type, &PyGObject_Type)) return false; - if (!py_data_type_define_constants(type)) + if (!define_analysis_data_type_constants(type)) return false; } @@ -501,3 +1037,34 @@ int convert_to_data_type(PyObject *arg, void *dst) 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 type de donnée ou NULL. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_data_type_or_none(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + if (arg == Py_None) + { + *((GDataType **)dst) = NULL; + result = 1; + } + + else + result = convert_to_data_type(arg, dst); + + return result; + +} diff --git a/plugins/pychrysalide/analysis/type.h b/plugins/pychrysalide/analysis/type.h index 8ea1096..9997a83 100644 --- a/plugins/pychrysalide/analysis/type.h +++ b/plugins/pychrysalide/analysis/type.h @@ -40,6 +40,9 @@ bool ensure_python_data_type_is_registered(void); /* Tente de convertir en type de donnée. */ int convert_to_data_type(PyObject *, void *); +/* Tente de convertir en type de donnée ou NULL. */ +int convert_to_data_type_or_none(PyObject *, void *); + #endif /* _PLUGINS_PYCHRYSALIDE_ANALYSIS_TYPE_H */ diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index 25ce772..870035d 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -648,6 +648,56 @@ PyObject *py_return_none(PyObject *self, PyObject *args) /****************************************************************************** * * +* Paramètres : self = objet quelconque. * +* args = arguments fournis à l'appel. * +* * +* Description : Retourne toujours faux. * +* * +* Retour : False. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *py_return_false(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + + result = Py_False; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet quelconque. * +* args = arguments fournis à l'appel. * +* * +* Description : Retourne toujours vrai. * +* * +* Retour : False. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *py_return_true(PyObject *self, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + + result = Py_True; + Py_INCREF(result); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : self = objet quelconque dont le code Python hérite. * * closure = non utilisé ici. * * * diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h index f5b5781..45b3083 100644 --- a/plugins/pychrysalide/helpers.h +++ b/plugins/pychrysalide/helpers.h @@ -104,6 +104,19 @@ bool register_python_module_object(PyObject *, PyTypeObject *); #name "(" args ")\n--\n\n" doc \ } +#define PYTHON_FALSE_WRAPPER_DEF(name, args, flags, doc)\ + { \ + #name, (PyCFunction)py_return_false, \ + flags, \ + #name "(" args ")\n--\n\n" doc \ + } + +#define PYTHON_TRUE_WRAPPER_DEF(name, args, flags, doc)\ + { \ + #name, (PyCFunction)py_return_true, \ + flags, \ + #name "(" args ")\n--\n\n" doc \ + } #define PYTHON_GETSET_DEF(name, get, set, doc, closure) \ { \ @@ -118,6 +131,9 @@ bool register_python_module_object(PyObject *, PyTypeObject *); #define PYTHON_HAS_DEF_FULL(name, base, doc) \ PYTHON_GETSET_DEF(#name, base ## _has_ ## name, NULL, doc, NULL) +#define PYTHON_RAWGET_DEF_FULL(name, base, doc) \ + PYTHON_GETSET_DEF(#name, base ## _ ## name, NULL, doc, NULL) + #define PYTHON_GET_DEF_FULL(name, base, doc) \ PYTHON_GETSET_DEF(#name, base ## _get_ ## name, NULL, doc, NULL) @@ -151,6 +167,12 @@ PyObject *not_yet_implemented_method(PyObject *, PyObject *); /* Retourne toujours rien. */ PyObject *py_return_none(PyObject *, PyObject *); +/* Retourne toujours faux. */ +PyObject *py_return_false(PyObject *, PyObject *); + +/* Retourne toujours vrai. */ +PyObject *py_return_true(PyObject *, PyObject *); + /* Marque l'absence d'implémentation pour un attribut donné. */ PyObject *not_yet_implemented_getter(PyObject *, void *); diff --git a/src/analysis/type-int.h b/src/analysis/type-int.h index f6d931b..f553bf3 100644 --- a/src/analysis/type-int.h +++ b/src/analysis/type-int.h @@ -35,6 +35,9 @@ typedef GDataType * (* type_dup_fc) (const GDataType *); /* Décrit le type fourni sous forme de caractères. */ typedef char * (* type_to_string_fc) (const GDataType *, bool); +/* Indique si le type assure une gestion des espaces de noms. */ +typedef bool (* type_handle_ns_fc) (const GDataType *); + /* Indique si le type est un pointeur. */ typedef bool (* type_is_pointer_fc) (const GDataType *); @@ -60,11 +63,10 @@ struct _GDataTypeClass { GObjectClass parent; /* A laisser en premier */ - bool handle_ns; /* Gestion au niveau de base ? */ - type_dup_fc dup; /* Copie d'instance existante */ type_to_string_fc to_string; /* Conversion au format texte */ + type_handle_ns_fc handle_ns; /* Gestion des espaces de noms?*/ type_is_pointer_fc is_pointer; /* Représentation de pointeur ?*/ type_is_reference_fc is_reference; /* Représentation de référence?*/ diff --git a/src/analysis/type.c b/src/analysis/type.c index 5bcb0e2..f19d846 100644 --- a/src/analysis/type.c +++ b/src/analysis/type.c @@ -72,8 +72,6 @@ static void g_data_type_class_init(GDataTypeClass *klass) object->dispose = (GObjectFinalizeFunc/* ! */)g_data_type_dispose; object->finalize = (GObjectFinalizeFunc)g_data_type_finalize; - klass->handle_ns = true; - } @@ -200,7 +198,7 @@ char *g_data_type_to_string(const GDataType *type, bool include) result = class->to_string(type, include); - if (include && type->namespace != NULL && class->handle_ns) + if (include && type->namespace != NULL && g_data_type_handle_namespaces(type)) { namespace = g_data_type_to_string(type->namespace, true); @@ -369,6 +367,35 @@ TypeQualifier g_data_type_get_qualifiers(const GDataType *type) * * * Paramètres : type = type à consulter. * * * +* Description : Indique si le type assure une gestion des espaces de noms. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool g_data_type_handle_namespaces(const GDataType *type) +{ + bool result; /* Bilan à retourner */ + GDataTypeClass *class; /* Classe du type */ + + class = G_DATA_TYPE_GET_CLASS(type); + + if (class->handle_ns != NULL) + result = class->handle_ns(type); + else + result = true; + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : type = type à consulter. * +* * * Description : Indique si le type est un pointeur. * * * * Retour : Bilan de la consultation. * diff --git a/src/analysis/type.h b/src/analysis/type.h index 431c080..000c557 100644 --- a/src/analysis/type.h +++ b/src/analysis/type.h @@ -87,6 +87,9 @@ void g_data_type_add_qualifier(GDataType *, TypeQualifier); /* Fournit les qualificatifs associés à une instance de type. */ TypeQualifier g_data_type_get_qualifiers(const GDataType *); +/* Indique si le type assure une gestion des espaces de noms. */ +bool g_data_type_handle_namespaces(const GDataType *); + /* Indique si le type est un pointeur. */ bool g_data_type_is_pointer(const GDataType *); diff --git a/src/analysis/types/proto.c b/src/analysis/types/proto.c index 362bd88..17f0262 100644 --- a/src/analysis/types/proto.c +++ b/src/analysis/types/proto.c @@ -72,6 +72,9 @@ static GDataType *g_proto_type_dup(const GProtoType *); /* Décrit le type fourni sous forme de caractères. */ static char *g_proto_type_to_string(const GProtoType *, bool); +/* Indique si le type assure une gestion des espaces de noms. */ +static bool g_proto_type_handle_namespaces(GProtoType *); + /* Indique le type défini pour un prototype. */ @@ -102,11 +105,11 @@ static void g_proto_type_class_init(GProtoTypeClass *klass) type = G_DATA_TYPE_CLASS(klass); - type->handle_ns = false; - type->dup = (type_dup_fc)g_proto_type_dup; type->to_string = (type_to_string_fc)g_proto_type_to_string; + type->handle_ns = (type_handle_ns_fc)g_proto_type_handle_namespaces; + } @@ -321,6 +324,29 @@ static char *g_proto_type_to_string(const GProtoType *type, bool include) /****************************************************************************** * * +* Paramètres : type = type à consulter. * +* * +* Description : Indique si le type assure une gestion des espaces de noms. * +* * +* Retour : Bilan de la consultation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool g_proto_type_handle_namespaces(GProtoType *type) +{ + bool result; /* Bilan à retourner */ + + result = false; + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : type = type à mettre à jour. * * ret = indication sur le type de retour. * * * diff --git a/tests/analysis/type.py b/tests/analysis/type.py new file mode 100644 index 0000000..5106cbc --- /dev/null +++ b/tests/analysis/type.py @@ -0,0 +1,92 @@ +#!/usr/bin/python3-dbg +# -*- coding: utf-8 -*- + + +from chrysacase import ChrysalideTestCase +from pychrysalide.analysis import DataType + + +class TestDataType(ChrysalideTestCase): + """TestCase for analysis.DataType.""" + + + def testTypeSubclassing(self): + """Verify the data type subclassing is working.""" + + class MyType(DataType): + + def __init__(self, num): + super(MyType, self).__init__() + self._num = num + + def _to_string(self, include): + return '%x' % self._num + + def _dup(self): + return MyType(self._num) + + tp = MyType(0x123) + + self.assertEqual(str(tp), '123') + + tp2 = tp.dup() + + self.assertEqual(str(tp), str(tp2)) + + + def testTypeDefaultProperties(self): + """Check for default values of some type properties.""" + + class MyPropType(DataType): + pass + + tp = MyPropType() + + self.assertTrue(tp.handle_namespaces) + + self.assertFalse(tp.is_pointer) + + self.assertFalse(tp.is_reference) + + class MyPropType2(DataType): + + def _handle_namespaces(self): + return True + + def _is_pointer(self): + return 123 < 1234 + + def _is_reference(self): + return False + + tp2 = MyPropType2() + + self.assertTrue(tp.handle_namespaces) + + self.assertTrue(tp2.is_pointer) + + self.assertFalse(tp2.is_reference) + + + def testTypeNamespaces(self): + """Test the type namespace property.""" + + class MyNSType(DataType): + + def __init__(self, name): + super(MyNSType, self).__init__() + self._name = name + + def _to_string(self, include): + return self._name + + tp = MyNSType('TP') + ns = MyNSType('NS') + + self.assertIsNone(tp.namespace) + + tp.namespace = (ns, '.') + + self.assertEqual(str(tp), 'NS.TP') + + self.assertEqual(tp.namespace, (ns, '.')) -- cgit v0.11.2-87-g4458