diff options
Diffstat (limited to 'plugins/pychrysalide/helpers.c')
-rw-r--r-- | plugins/pychrysalide/helpers.c | 473 |
1 files changed, 473 insertions, 0 deletions
diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c new file mode 100644 index 0000000..17a396d --- /dev/null +++ b/plugins/pychrysalide/helpers.c @@ -0,0 +1,473 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * helpers.c - simplification des interactions de base avec Python + * + * Copyright (C) 2012-2017 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 Foobar. If not, see <http://www.gnu.org/licenses/>. + */ + + +#include "helpers.h" + + +#include <assert.h> +#include <pygobject.h> +#include <stdarg.h> +#include <string.h> + + + +/* ---------------------------------------------------------------------------------- */ +/* ACCELERATEURS POUR PYTHON UNIQUEMENT */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : status = bilan de comparaison à traduire. * +* op = type de comparaison menée. * +* * +* Description : Traduit pour Python le bilan d'une comparaison riche. * +* * +* Retour : Objet Python à référencer. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *status_to_rich_cmp_state(int status, int op) +{ + PyObject *result; /* Bilan àretourner */ + + switch (op) + { + case Py_LT: + result = status < 0 ? Py_True : Py_False; + break; + + case Py_LE: + result = status <= 0 ? Py_True : Py_False; + break; + + case Py_EQ: + result = status == 0 ? Py_True : Py_False; + break; + + case Py_NE: + result = status != 0 ? Py_True : Py_False; + break; + + case Py_GT: + result = status > 0 ? Py_True : Py_False; + break; + + case Py_GE: + result = status >= 0 ? Py_True : Py_False; + break; + + default: + result = Py_NotImplemented; + break; + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : target = propriétaire de la routine visée. * +* method = désignation de la fonction à appeler. * +* * +* Description : Indique si une routine Python existe ou non. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool has_python_method(PyObject *module, const char *method) +{ + bool result; /* Bilan à retourner */ + PyObject *func; /* Fonction visée */ + + func = PyObject_GetAttrString(module, method); + if (func == NULL) return false; + + result = PyCallable_Check(func); + + Py_DECREF(func); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : func = fonction Python à appeler. * +* args = arguments à associer à l'opération. * +* * +* Description : Appelle une routine Python. * +* * +* Retour : Retour obtenu ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *_run_python_method(PyObject *func, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + + result = NULL; + + if (PyCallable_Check(func)) + { + result = PyObject_CallObject(func, args); + if (result == NULL) PyErr_Print(); + } + else if (PyErr_Occurred()) PyErr_Print(); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : target = propriétaire de la routine visée. * +* method = désignation de la fonction à appeler. * +* args = arguments à associer à l'opération. * +* * +* Description : Appelle une routine Python. * +* * +* Retour : Retour obtenu ou NULL si erreur. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyObject *run_python_method(PyObject *module, const char *method, PyObject *args) +{ + PyObject *result; /* Bilan à retourner */ + PyObject *func; /* Fonction visée */ + + result = NULL; + + func = PyObject_GetAttrString(module, method); + if (func == NULL) return NULL; + + result = _run_python_method(func, args); + + Py_DECREF(func); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : obj_type = type dont le dictionnaire est à compléter. * +* key = désignation de la constante à intégrer. * +* value = valeur de la constante à intégrer. * +* * +* Description : Ajoute une constante au dictionnaire d'un type Python donné. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool PyDict_AddIntConstant(PyTypeObject *obj_type, const char *key, long value) +{ + bool result; /* Bilan à retourner */ + PyObject *item; /* Nouvel élément à insérer */ + int ret; /* Bilan d'un ajout */ + + item = PyLong_FromLong(value); + + ret = PyDict_SetItemString(obj_type->tp_dict, key, item); + result = (ret != -1); + + Py_DECREF(item); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : obj_type = type dont le dictionnaire est à compléter. * +* key = désignation de la constante à intégrer. * +* value = valeur de la constante à intégrer. * +* * +* Description : Ajoute une constante au dictionnaire d'un type Python donné. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool PyDict_AddStringConstant(PyTypeObject *obj_type, const char *key, const char *value) +{ + bool result; /* Bilan à retourner */ + PyObject *item; /* Nouvel élément à insérer */ + int ret; /* Bilan d'un ajout */ + + item = PyUnicode_FromString(value); + + ret = PyDict_SetItemString(obj_type->tp_dict, key, item); + result = (ret != -1); + + Py_DECREF(item); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONFORTS CIBLANT PYGOBJECT */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : spec = définition à mettre en place dynamiquement. * +* * +* Description : Définit dans le tas de Python un nouveau type. * +* * +* Retour : Nouveau type prêt à emploi. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *define_python_dynamic_type(const PyTypeObject *spec) +{ + PyTypeObject *result; /* Définition créée à renvoyer */ + PyTypeObject *type; /* Type de tous les types */ + size_t size; /* Taille de la définition */ + char *s; /* Marqueur de début de chaîne */ + PyHeapTypeObject *hobj; /* Version réelle du type créé */ + + /** + * Le cahier des charges est ici d'éviter les erreurs suivantes : + * + * TypeError: type 'XXX' is not dynamically allocated but its base type 'YYY' is dynamically allocated + * Fatal Python error: unexpected exception during garbage collection + * + * L'allocation dynamique est marquée par le fanion Py_TPFLAGS_HEAPTYPE. + * + * Une des rares fonctions qui appliquent ce fanion est PyType_FromSpecWithBases(), + * mais elle appelle ensuite PyType_Ready(), ce qui est incompatible avec des modifications + * utltérieures avant un appel à pygobject_register_class(). + * + * Le code suivant s'inspire fortement des méthodes originales de Python, + * dont les mécanismes employés par PyType_GenericAlloc(). + */ + + type = &PyType_Type; + size = _PyObject_SIZE(type); + + if (PyType_IS_GC(type)) + result = (PyTypeObject *)_PyObject_GC_Malloc(size); + else + result = (PyTypeObject *)PyObject_MALLOC(size); + + if (type->tp_flags & Py_TPFLAGS_HEAPTYPE) + Py_INCREF(type); + + /* Définitions sommaires */ + + memset(result, 0, sizeof(PyHeapTypeObject)); + + memcpy(result, spec, sizeof(PyTypeObject)); + + result->tp_flags |= Py_TPFLAGS_HEAPTYPE; + + /* Définitions des noms */ + + /** + * Pour un type dynamique, les désignations ne s'appuient pas sur la partie réservée + * au type, mais sur les données suivantes (cf. type_name() et type_qualname()). + * + * Les deux fonctions désignées sont par ailleurs facilement accessibles : + * + * #0 0x0000555555689bf0 in type_qualname (...) at Objects/typeobject.c:393 + * #1 0x000055555568b3b4 in type_repr (...) at Objects/typeobject.c:855 + * #2 0x0000555555693574 in object_str (...) at Objects/typeobject.c:3511 + * #3 0x0000555555670d02 in PyObject_Str (...) at Objects/object.c:535 + * ... + * + * On s'inspire donc du contenu de PyType_FromSpecWithBases() pour éviter tout + * plantage du fait de données non initialisées. + */ + + hobj = (PyHeapTypeObject *)result; + + s = strrchr(spec->tp_name, '.'); + + if (s == NULL) + s = (char *)spec->tp_name; + else + s++; + + hobj->ht_name = PyUnicode_FromString(s); + assert(hobj->ht_name != NULL); + + hobj->ht_qualname = hobj->ht_name; + Py_INCREF(hobj->ht_qualname); + + result->tp_as_async = &hobj->as_async; + result->tp_as_number = &hobj->as_number; + result->tp_as_sequence = &hobj->as_sequence; + result->tp_as_mapping = &hobj->as_mapping; + result->tp_as_buffer = &hobj->as_buffer; + + hobj->ht_cached_keys = _PyDict_NewKeysForClass(); + + + + +#if 0 + if (type->tp_itemsize == 0) + (void)PyObject_INIT(result, type); + else + (void) PyObject_INIT_VAR((PyVarObject *)result, type, 0); + + if (PyType_IS_GC(type)) + _PyObject_GC_TRACK(result); +#endif + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : module = module où conserver une référence au type créé. * +* gtype = type dans sa version GLib. * +* type = type dans sa version Python. * +* base = type de base de l'objet. * +* * +* Description : Enregistre correctement une surcouche de conversion GObject. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool _register_class_for_pygobject(PyObject *dict, GType gtype, PyTypeObject *type, PyTypeObject *base, ...) +{ + bool result; /* Bilan à retourner */ + Py_ssize_t size; /* Taille de liste actuelle */ + PyObject *static_bases; /* Base(s) de l'objet */ + va_list ap; /* Parcours des arguments */ + PyTypeObject *static_base; /* Base à rajouter à la liste */ + + /** + * pygobject_register_class() définit type->tp_base à partir des arguments fournis, + * puis fait appel à PyType_Ready(). + * + * PyType_Ready() complète la définition via inherit_special() : + * + * type->tp_basicsize = type->tp_base->tp_basicsize + * + * Cependant, il y a un appel à mro_internal() avant, qui mène à solid_base() + * puis à extra_ivars(). Et là : + * + * size_t t_size = type->tp_basicsize; + * size_t b_size = base->tp_basicsize; + * + * assert(t_size >= b_size); + * + * Si le type de base est spécifié, une taille doit être indiquée. + * + * Et quelqu'un doit se coller à la tâche. PyGObject ne fait rien, donc... + */ + + if (type->tp_basicsize < base->tp_basicsize) + { + assert(type->tp_basicsize == 0); + type->tp_basicsize = base->tp_basicsize; + } + + size = 1; + static_bases = PyTuple_New(size); + + Py_INCREF(base); + PyTuple_SetItem(static_bases, 0, (PyObject *)base); + + va_start(ap, base); + + while (1) + { + static_base = va_arg(ap, PyTypeObject *); + + if (static_base == NULL) break; + + _PyTuple_Resize(&static_bases, ++size); + + Py_INCREF(static_base); + PyTuple_SetItem(static_bases, size - 1, (PyObject *)static_base); + + } + + va_end(ap); + + + + /* +#0 0x000055555565aad4 in insertdict (mp=0x7fffe7c2c8a8, key='GenConfig', hash=262970853803706525, value=<unknown at remote 0x7ffff6bd9ce0>) at ../Objects/dictobject.c:801 +#1 0x000055555565bf62 in PyDict_SetItem (op={'__doc__': 'Python module for Chrysalide.glibext', '__name__': 'pychrysalide.glibext', 'BufferLine': <type at remote 0x7ffff6bd9760>, '__spec__': None, 'ConfigParam': <type at remote 0x7ffff6bd9a00>, '__package__': None, 'Buffercache': <type at remote 0x7ffff6bd95c0>, 'ConfigParamIterator': <type at remote 0x7ffff6bd9fc0>, '__loader__': None}, key='GenConfig', value=<unknown at remote 0x7ffff6bd9ce0>) at ../Objects/dictobject.c:1227 +#2 0x00005555556610b0 in PyDict_SetItemString (v={'__doc__': 'Python module for Chrysalide.glibext', '__name__': 'pychrysalide.glibext', 'BufferLine': <type at remote 0x7ffff6bd9760>, '__spec__': None, 'ConfigParam': <type at remote 0x7ffff6bd9a00>, '__package__': None, 'Buffercache': <type at remote 0x7ffff6bd95c0>, 'ConfigParamIterator': <type at remote 0x7ffff6bd9fc0>, '__loader__': None}, key=0x7ffff69cd0bd "GenConfig", item=<unknown at remote 0x7ffff6bd9ce0>) at ../Objects/dictobject.c:2870 +#3 0x00007ffff69b3d12 in _register_class_for_pygobject (dict={'__doc__': 'Python module for Chrysalide.glibext', '__name__': 'pychrysalide.glibext', 'BufferLine': <type at remote 0x7ffff6bd9760>, '__spec__': None, 'ConfigParam': <type at remote 0x7ffff6bd9a00>, '__package__': None, 'Buffercache': <type at remote 0x7ffff6bd95c0>, 'ConfigParamIterator': <type at remote 0x7ffff6bd9fc0>, '__loader__': None}, gtype=93824998785328, type=0x7ffff6bd9ce0 <py_generic_config_type>, base=0x7fffe80e72a0 <PyGObject_Type>) at helpers.c:320 + */ + + + + //type->tp_weaklistoffset = offsetof(PyGObject, weakreflist); + //type->tp_dictoffset = offsetof(PyGObject, inst_dict); + + pygobject_register_class(dict, NULL, gtype, type, static_bases); + + if (PyErr_Occurred() == NULL) + result = true; + + else + { + PyErr_Print(); + result = false; + } + + assert(PyErr_Occurred() == NULL); + + return result; + +} |