/* 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
/* ---------------------------------------------------------------------------------- */
/* 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=) 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;
}