/* 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 Chrysalide. If not, see . */ #include "helpers.h" #include #include #include #include #include #include "access.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; } /* ---------------------------------------------------------------------------------- */ /* MISE EN PLACE DE MODULES */ /* ---------------------------------------------------------------------------------- */ /****************************************************************************** * * * Paramètres : super = module dont la définition est à compléter. * * def = définition du module à créer. * * * * Description : Met en place un nouveau module Python. * * * * Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ PyObject *build_python_module(PyObject *super, PyModuleDef *def) { PyObject *result; /* Création à retourner */ int ret; /* Bilan d'un appel */ char *dot; /* Dernier point de ce chemin */ result = PyModule_Create(def); if (result == NULL) goto quick_bad_exit; ret = PyState_AddModule(super, def); if (ret != 0) goto bad_exit; ret = _PyImport_FixupBuiltin(result, def->m_name); if (ret != 0) goto bad_exit; dot = strrchr(def->m_name, '.'); assert(dot != NULL); Py_INCREF(result); ret = PyModule_AddObject(super, dot + 1, result); if (ret != 0) goto bad_exit; register_access_to_python_module(def->m_name, result); return result; bad_exit: Py_DECREF(result); quick_bad_exit: assert(false); return false; } /* ---------------------------------------------------------------------------------- */ /* 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=) at ../Objects/dictobject.c:801 #1 0x000055555565bf62 in PyDict_SetItem (op={'__doc__': 'Python module for Chrysalide.glibext', '__name__': 'pychrysalide.glibext', 'BufferLine': , '__spec__': None, 'ConfigParam': , '__package__': None, 'Buffercache': , 'ConfigParamIterator': , '__loader__': None}, key='GenConfig', value=) at ../Objects/dictobject.c:1227 #2 0x00005555556610b0 in PyDict_SetItemString (v={'__doc__': 'Python module for Chrysalide.glibext', '__name__': 'pychrysalide.glibext', 'BufferLine': , '__spec__': None, 'ConfigParam': , '__package__': None, 'Buffercache': , 'ConfigParamIterator': , '__loader__': None}, key=0x7ffff69cd0bd "GenConfig", item=) at ../Objects/dictobject.c:2870 #3 0x00007ffff69b3d12 in _register_class_for_pygobject (dict={'__doc__': 'Python module for Chrysalide.glibext', '__name__': 'pychrysalide.glibext', 'BufferLine': , '__spec__': None, 'ConfigParam': , '__package__': None, 'Buffercache': , 'ConfigParamIterator': , '__loader__': None}, gtype=93824998785328, type=0x7ffff6bd9ce0 , base=0x7fffe80e72a0 ) 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; }