diff options
56 files changed, 2820 insertions, 1257 deletions
diff --git a/configure.ac b/configure.ac index 23e4e9b..2ff719a 100644 --- a/configure.ac +++ b/configure.ac @@ -593,6 +593,16 @@ AC_SUBST(LIBHS_LIBS) #--- Checks for json-glib-1.0 +AM_CONDITIONAL([BUILD_JSONGLIB_SUPPORT], [test "x$enable_jsonglib_support" = "xyes"]) + +if test "x$BUILD_JSONGLIB_SUPPORT_TRUE" = "x"; then + # json-glib support is available and enabled + CPPFLAGS="$CPPFLAGS -DINCLUDE_JSONGLIB_SUPPORT" +fi + +curl + + PKG_CHECK_MODULES(LIBJSONGLIB,json-glib-1.0 >= 1.6.6,[libjsonglib_found=yes],[libjsonglib_found=no]) if test "$libjsonglib_found" = "yes"; then @@ -601,8 +611,14 @@ else libjsonglib_version='-' fi -AC_SUBST(LIBJSONGLIB_CFLAGS) -AC_SUBST(LIBJSONGLIB_LIBS) +if test "x$enable_json_glib_support" = "xyes"; then + + AC_SUBST(LIBJSONGLIB_CFLAGS) + AC_SUBST(LIBJSONGLIB_LIBS) + + true # empty if/then body not allowed + +fi #--- Checks for Python diff --git a/plugins/pe/core.c b/plugins/pe/core.c index 42f712d..9d30a34 100644 --- a/plugins/pe/core.c +++ b/plugins/pe/core.c @@ -62,8 +62,8 @@ static void g_pe_plugin_finalize(GPePlugin *); /* Prend acte de l'activation du greffon. */ static bool g_pe_plugin_enable(GPePlugin *); -/* Prend acte de l'extinction du greffon. */ -static void g_pe_plugin_disable(GPePlugin *); +/* Prend acte de la désactivation du greffon. */ +static bool g_pe_plugin_disable(GPePlugin *); @@ -288,16 +288,21 @@ static bool g_pe_plugin_enable(GPePlugin *plugin) * * * Paramètres : plugin = greffon à manipuler. * * * -* Description : Prend acte de l'extinction du greffon. * +* Description : Prend acte de la désactivation du greffon. * * * -* Retour : - * +* Retour : Bilan de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void g_pe_plugin_disable(GPePlugin *plugin) +static bool g_pe_plugin_disable(GPePlugin *plugin) { + bool result; /* Bilan à retourner */ + + result = true; + + return result; } diff --git a/plugins/pychrysalide/analysis/content.c b/plugins/pychrysalide/analysis/content.c index dd9c1c1..c271139 100644 --- a/plugins/pychrysalide/analysis/content.c +++ b/plugins/pychrysalide/analysis/content.c @@ -50,9 +50,9 @@ /* Initialise la classe générique des contenus de binaire. */ -static void py_binary_content_init_gclass(GBinContentClass *, gpointer); +static int py_binary_content_init_gclass(GBinContentClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT, py_binary_content_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(binary_content, G_TYPE_BIN_CONTENT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_binary_content_init(PyObject *, PyObject *, PyObject *); @@ -163,37 +163,39 @@ static PyObject *py_binary_content_get_data(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe générique des contenus de binaire. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_binary_content_init_gclass(GBinContentClass *class, gpointer unused) +static int py_binary_content_init_gclass(GBinContentClass *gclass, PyTypeObject *pyclass) { - class->describe = py_binary_content_describe_wrapper; + PY_CLASS_SET_WRAPPER(gclass->describe, py_binary_content_describe_wrapper); + + PY_CLASS_SET_WRAPPER(gclass->compute_checksum, py_binary_content_compute_checksum_wrapper); - class->compute_checksum = py_binary_content_compute_checksum_wrapper; + PY_CLASS_SET_WRAPPER(gclass->compute_size, py_binary_content_compute_size_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_start_pos, py_binary_content_compute_start_pos_wrapper); + PY_CLASS_SET_WRAPPER(gclass->compute_end_pos, py_binary_content_compute_end_pos_wrapper); - class->compute_size = py_binary_content_compute_size_wrapper; - class->compute_start_pos = py_binary_content_compute_start_pos_wrapper; - class->compute_end_pos = py_binary_content_compute_end_pos_wrapper; + PY_CLASS_SET_WRAPPER(gclass->seek, py_binary_content_seek_wrapper); - class->seek = py_binary_content_seek_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_raw, py_binary_content_read_raw_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u8, py_binary_content_read_u8_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u16, py_binary_content_read_u16_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u32, py_binary_content_read_u32_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_u64, py_binary_content_read_u64_wrapper); - class->read_raw = py_binary_content_read_raw_wrapper; - class->read_u8 = py_binary_content_read_u8_wrapper; - class->read_u16 = py_binary_content_read_u16_wrapper; - class->read_u32 = py_binary_content_read_u32_wrapper; - class->read_u64 = py_binary_content_read_u64_wrapper; + PY_CLASS_SET_WRAPPER(gclass->read_uleb128, py_binary_content_read_uleb128_wrapper); + PY_CLASS_SET_WRAPPER(gclass->read_leb128, py_binary_content_read_leb128_wrapper); - class->read_uleb128 = py_binary_content_read_uleb128_wrapper; - class->read_leb128 = py_binary_content_read_leb128_wrapper; + return 0; } @@ -2248,6 +2250,8 @@ bool ensure_python_binary_content_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_BIN_CONTENT, (PyGClassInitFunc)py_binary_content_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_BIN_CONTENT, type)) return false; diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c index f715a8e..7e87e27 100644 --- a/plugins/pychrysalide/bindings.c +++ b/plugins/pychrysalide/bindings.c @@ -25,18 +25,18 @@ #include "bindings.h" -#ifdef PYTHON_PACKAGE -# include <dlfcn.h> -#endif +#include <assert.h> +#include <dlfcn.h> #include <pygobject.h> +#include <stddef.h> #include <stdio.h> -#include <config.h> #include <common/cpp.h> #include <common/extstr.h> -#include <plugins/pglist.h> // REMME ? -#include <plugins/self.h> // REMME ? +#include <core/core.h> +#include <plugins/pglist.h> +#include <plugins/self.h> #include "access.h" @@ -106,6 +106,42 @@ static bool install_metaclass_for_python_gobjects(void); /* Met en place un environnement pour l'extension Python. */ static bool setup_python_context(void); +/* Intègre les éventuelles fonctions natives des interfaces. */ +static void inherit_interface_slots(PyObject *); + +/** + * Conservation d'anciens pointeurs remplacés + */ +static initproc __old_gobject_meta_base_init = NULL; +static initproc __old_gobject_meta_init = NULL; + +/** + * La fonction unhook_pygobject_behaviour(), inversant les opérations de la fonction + * unhook_pygobject_behaviour() manipulerait volontiers les fonctions PyImport_ImportModule() + * et PyObject_GetAttrString(). + * + * Cependant, les appels à ces dernières depuis la clôture organisée par la fonction + * PyExit_pychrysalide() provoque l'erreur suivante : + * + * Fatal Python error: _PyInterpreterState_GET: the function must be called with the GIL held, but the GIL is released (the current Python thread state is NULL) + * + * Les accès nécessaires sont donc conservés ici. + */ +static PyTypeObject *__gobject_meta_base = NULL; +static PyTypeObject *__gobject_meta = NULL; + +/* Interceptionne une initialisation pour types gi._gi.GObject. */ +static int hook_gobject_meta_base_init(PyObject *, PyObject *, PyObject *); + +/* Interceptionne une initialisation pour types GObject.Object. */ +static int hook_gobject_meta_init(PyObject *, PyObject *, PyObject *); + +/* Modifie légèrement le comportement des GObjects en Python. */ +static bool hook_pygobject_behaviour(void); + +/* Restaure le comportement d'origine des GObjects en Python. */ +static void unhook_pygobject_behaviour(void); + /* Assure la définition d'un type GObject pour Python adapté. */ static void ensure_native_pygobject_type(PyTypeObject **); @@ -125,6 +161,8 @@ static void restore_original_pygobject_type(PyTypeObject *); /* ------------------------ FONCTIONS GLOBALES DE CHRYSALIDE ------------------------ */ +/* Assure le plein chargement dans un interpréteur Python. */ +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *); /* Point de sortie pour l'initialisation de Python. */ static void PyExit_pychrysalide(void); @@ -503,6 +541,265 @@ static bool setup_python_context(void) /****************************************************************************** * * +* Paramètres : cls = classe instanciée pour la construction d'un objet. * +* * +* Description : Intègre les éventuelles fonctions natives des interfaces. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void inherit_interface_slots(PyObject *cls) +{ + GType gtype; /* Type GObject lié au Python */ + GType *ifaces; /* Interfaces implémentées */ + guint ifaces_count; /* Nombre de ces interfaces */ + guint i; /* Boucle de parcours */ + PyTypeObject *iface_type; /* Type Python pour interface */ + size_t k; /* Boucle de parcours */ + size_t offset; /* Position dans une structure */ + void *src_slot; /* Eventuelle fonction idéale */ + void *dst_slot; /* Eventuelle fonction en place*/ + + static size_t slot_offsets[] = { /* Emplacements à actualiser */ + //offsetof(PyTypeObject, tp_str), + offsetof(PyTypeObject, tp_hash), + offsetof(PyTypeObject, tp_richcompare), + }; + + /** + * Cette fonction reprend les principes de la fonction d'importation de + * PyGObject pygobject_inherit_slots(). + * + * Cependant, cette dernière n'est appelée que depuis les fonctions : + * - pygobject_register_class() (send C -> Python), qui peut écraser des + * slots existants ; + * - pygobject_new_with_interfaces() / pygobject_lookup_class(), qui ne + * remplace pas les fonctions par défaut déjà en place. + * + * Pour mémoire, les types créés dynamiquement depuis des scripts (sens + * Python -> C) passent par la fonction _wrap_pyg_type_register(). + */ + + gtype = pyg_type_from_object(cls); + assert(gtype != G_TYPE_INVALID); + + ifaces = g_type_interfaces(gtype, &ifaces_count); + + for (i = 0; i < ifaces_count; i++) + { + iface_type = pygobject_lookup_class(ifaces[i]); + +#define PYTYPE_SLOT(tp, off) \ + *(void **)(void *)(((char *)tp) + off) + + for (k = 0; k < ARRAY_SIZE(slot_offsets); k++) + { + offset = slot_offsets[k]; + + src_slot = PYTYPE_SLOT(iface_type, offset); + + if (src_slot == NULL) + continue; + + if (src_slot == PYTYPE_SLOT(&PyBaseObject_Type, offset) + || src_slot == PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + + dst_slot = PYTYPE_SLOT(cls, offset); + + if (src_slot == dst_slot) + continue; + + if (dst_slot != NULL) + { + if (dst_slot != PYTYPE_SLOT(&PyBaseObject_Type, offset) + && dst_slot != PYTYPE_SLOT(&PyGObject_Type, offset)) + continue; + } + + /** + * Usage du *(void **)(void *) + */ + PYTYPE_SLOT(cls, offset) = src_slot; + + } + + } + + g_free(ifaces); + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types gi._gi.GObject. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_base_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici _GObjectMetaBase. + */ + + result = __old_gobject_meta_base_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : self = objet à initialiser (théoriquement). * +* args = arguments fournis à l'appel. * +* kwds = arguments de type key=val fournis. * +* * +* Description : Interceptionne une initialisation pour types GObject.Object. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int hook_gobject_meta_init(PyObject *self, PyObject *args, PyObject *kwds) +{ + int result; /* Bilan à retourner */ + + /** + * Le type de self (self->ob_type->tp_name) est ici GObjectMeta. + */ + + result = __old_gobject_meta_init(self, args, kwds); + + if (result == 0) + inherit_interface_slots(self); + + return result; + + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Modifie légèrement le comportement des GObjects en Python. * +* * +* Retour : Bilan de l'initialisation. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static bool hook_pygobject_behaviour(void) +{ + bool result; /* Bilan à retourner */ + PyObject *gi_types_mod; /* Module Python-GObject */ + + result = false; + + /** + * Validation des accès. + * + * Les références prises sur les attributs sont restituées dans + * unhook_pygobject_behaviour(). + */ + + gi_types_mod = PyImport_ImportModule("gi.types"); + if (gi_types_mod == NULL) goto exit; + + __gobject_meta_base = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "_GObjectMetaBase"); + assert(__gobject_meta_base != NULL); + if (__gobject_meta_base == NULL) goto exit_with_mod; + + __gobject_meta = (PyTypeObject *)PyObject_GetAttrString(gi_types_mod, "GObjectMeta"); + assert(__gobject_meta != NULL); + if (__gobject_meta == NULL) goto exit_with_mod; + + /** + * Modification des comportements. + */ + + __old_gobject_meta_base_init = __gobject_meta_base->tp_init; + + __gobject_meta_base->tp_init = hook_gobject_meta_base_init; + + __old_gobject_meta_init = __gobject_meta->tp_init; + + __gobject_meta->tp_init = hook_gobject_meta_init; + + result = true; + + exit_with_mod: + + Py_DECREF(gi_types_mod); + + exit: + + if (!result) + PyErr_SetString(PyExc_SystemError, "unable to hook the GObject behaviour in Python."); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Restaure le comportement d'origine des GObjects en Python. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void unhook_pygobject_behaviour(void) +{ + /** + * Le déclenchement de la fonction PyExit_pychrysalide() appelante est + * programmé depuis init_python_pychrysalide_module(), appelée si + * hook_pygobject_behaviour() a opéré avec réussite. + */ + assert(__gobject_meta_base != NULL); + assert(__gobject_meta != NULL); + + __gobject_meta_base->tp_init = __old_gobject_meta_base_init; + + Py_XDECREF(__gobject_meta_base); + + __gobject_meta->tp_init = __old_gobject_meta_init; + + Py_XDECREF(__gobject_meta); + +} + + +/****************************************************************************** +* * * Paramètres : namespace = module particulier à charger à partir de gi. * * version = idenfiant de la version à stipuler. * * * @@ -873,6 +1170,9 @@ PyObject *init_python_pychrysalide_module(const pyinit_details_t *details) if (!setup_python_context()) goto exit; + if (!hook_pygobject_behaviour()) + goto exit; + /** * Le chargement forcé de l'espace GLib pour Python permet d'éviter un écueil, * à savoir des types convertis de façon incomplète. Par exemple, pour une @@ -915,7 +1215,7 @@ PyObject *init_python_pychrysalide_module(const pyinit_details_t *details) PyErr_SetString(PyExc_SystemError, "failed to load all PyChrysalide components."); else if (details->standalone) - status = do_global_init(); + status = init_python_interpreter_for_standalone_mode(details); if (!status) { @@ -1060,13 +1360,11 @@ void log_pychrysalide_exception(const char *prefix, ...) /* ---------------------------------------------------------------------------------- */ - - /****************************************************************************** * * -* Paramètres : py_gobj_def = définition de type actuelle. [OUT] * +* Paramètres : details = précisions de chargement complémentaires. * * * -* Description : Restore une ancienne définition de type GObject au besoin. * +* Description : Assure le plein chargement dans un interpréteur Python. * * * * Retour : Bilan de l'opération. * * * @@ -1074,103 +1372,77 @@ void log_pychrysalide_exception(const char *prefix, ...) * * ******************************************************************************/ -bool do_global_init(void) +static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *details) { - - return true; - return false; - - -#if 0 - - bool result; /* Bilan à retourner */ int ret; /* Bilan de préparatifs */ -#ifdef PYTHON_PACKAGE Dl_info info; /* Informations dynamiques */ -#endif + GModule *module; /* Structure de chargement GLib*/ GPluginModule *self; /* Représentation interne */ - PluginStatusFlags self_flags; /* Fanions à mettre à jour */ + + result = false; ret = Py_AtExit(PyExit_pychrysalide); if (ret == -1) { PyErr_SetString(PyExc_SystemError, "failed to register a cleanup function."); - goto exit_and_restore; + goto exit; + } + + if (!load_core_components(ACC_ALL_COMPONENTS)) + { + PyErr_SetString(PyExc_SystemError, "unable to load core components."); + goto exit; } /** - * Si cette extension pour Python est chargée depuis un dépôt Python, - * elle ne se trouve pas dans le répertoire classique des extensions et - * n'est donc pas chargée et enregistrée comme attendu. + * Le module chargé par Python n'apparaît pas dans la liste des greffons de + * Chrysalide et ne peut donc pas être référencé comme dépendance par d'autres + * extensions. * - * Cet enregistrement est donc forcé ici. + * Par ailleurs, lors de la recherche d'autres greffons via l'appel à la + * fonction init_all_plugins() ci-après, il faut que le nom du greffon soit + * déjà réservé pour faire échouer le second chargement du greffon courant + * lors du parcours des répertoires conservant les fichiers d'extensions. */ -#ifdef PYTHON_PACKAGE - ret = dladdr(__FUNCTION__, &info); if (ret == 0) { LOG_ERROR_DL_N("dladdr"); - - // err msg - - - Py_DECREF(result); - result = NULL; - - goto exit_and_restore; - } - - self = g_plugin_module_new(info.dli_fname); - assert(self != NULL); - - register_plugin(self); - -#endif - - - if (!load_core_components(ACC_GLOBAL_VARS)) - { - PyErr_SetString(PyExc_SystemError, "unable to load core components."); + PyErr_SetString(PyExc_SystemError, "failed to force bindings registration."); goto exit; - } - init_all_plugins(false); + } - lock_plugin_list_for_reading(); + module = g_module_open(info.dli_fname, G_MODULE_BIND_LAZY); + assert(module != NULL); - self = get_plugin_by_name("PyChrysalide", NULL); - assert(self != NULL); + self = details->create_self(module); - self_flags = g_plugin_module_get_flags(self); - self_flags &= ~(PSF_FAILURE | PSF_LOADED); - self_flags |= (status ? PSF_LOADED : PSF_FAILURE); + /* A ce stade, le greffon a été chargé correctement */ + g_plugin_module_override_flags(self, PSF_LOADED); - g_plugin_module_override_flags(self, self_flags); + register_plugin(self); unref_object(self); - unlock_plugin_list_for_reading(); - - load_remaning_plugins(); - - + /** + * Intégration des fonctionnalités portées par d'autres greffons. + */ + result = true; + init_all_plugins(true); - done: + exit: return result; -#endif - } - /****************************************************************************** * * * Paramètres : - * @@ -1185,24 +1457,10 @@ bool do_global_init(void) static void PyExit_pychrysalide(void) { - //assert(_standalone); - - /* - extern void set_current_project(void *project); - - set_current_project(NULL); - */ - -#ifdef TRACK_GOBJECT_LEAKS - remember_gtypes_for_leaks(); -#endif + unhook_pygobject_behaviour(); exit_all_plugins(); - //unload_all_core_components(true); - -#ifdef TRACK_GOBJECT_LEAKS - dump_remaining_gtypes(); -#endif + unload_core_components(ACC_ALL_COMPONENTS); } diff --git a/plugins/pychrysalide/bindings.h b/plugins/pychrysalide/bindings.h index e9ee421..1758747 100644 --- a/plugins/pychrysalide/bindings.h +++ b/plugins/pychrysalide/bindings.h @@ -36,9 +36,13 @@ #include <Python.h> +#include <gmodule.h> #include <stdbool.h> +#include <plugins/plugin.h> + + /* Charge un module GI dans Python avec une version attendue. */ bool import_namespace_from_gi_repository(const char *, const char *); @@ -50,6 +54,12 @@ typedef struct _pyinit_details_t bool (* populate_extra) (void); /* Ajout de types ? */ + /** + * Prototype de la fonction de création, à garder synchronisé avec + * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h). + */ + GPluginModule * (* create_self) (GModule *); + } pyinit_details_t; /* Implémente le point d'entrée pour l'initialisation de Python. */ @@ -60,8 +70,4 @@ void log_pychrysalide_exception(const char *, ...); -bool do_global_init(void); - - - #endif /* _PLUGINS_PYCHRYSALIDE_BINDINGS_H */ diff --git a/plugins/pychrysalide/core-ui.c b/plugins/pychrysalide/core-ui.c index 32d3516..1b332b7 100644 --- a/plugins/pychrysalide/core-ui.c +++ b/plugins/pychrysalide/core-ui.c @@ -179,7 +179,7 @@ GPluginModule *g_pychrysalide_plugin_ui_new(GModule *module) { GPyChrysalidePluginUI *result; /* Structure à retourner */ - result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN, NULL); + result = g_object_new(G_TYPE_PYCHRYSALIDE_PLUGIN_UI, NULL); if (!g_pychrysalide_plugin_ui_create(result, module)) g_clear_object(&result); @@ -310,6 +310,7 @@ PyMODINIT_FUNC PyInit_pychrysalideui(void) details.standalone = _standalone; details.populate_extra = NULL; + details.create_self = g_pychrysalide_plugin_ui_new; result = init_python_pychrysalide_module(&details); diff --git a/plugins/pychrysalide/core.c b/plugins/pychrysalide/core.c index 3c551c7..0e72b46 100644 --- a/plugins/pychrysalide/core.c +++ b/plugins/pychrysalide/core.c @@ -306,68 +306,6 @@ bool g_pychrysalide_plugin_create(GPyChrysalidePlugin *plugin, GModule *module) - - - - - - - - - - - -#if 0 - - - -/****************************************************************************** -* * -* Paramètres : plugin = greffon à manipuler. * -* action = type d'action attendue. * -* type = type d'objet à mettre en place. * -* * -* Description : Crée une instance à partir d'un type dynamique externe. * -* * -* Retour : Instance d'objet gérée par l'extension ou NULL. * -* * -* Remarques : - * -* * -******************************************************************************/ - -G_MODULE_EXPORT gpointer chrysalide_plugin_build_type_instance(GPluginModule *plugin, PluginAction action, GType type) -{ - gpointer result; /* Instance à retourner */ - PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyTypeObject *pytype; /* Classe Python concernée */ - PyObject *instance; /* Initialisation forcée */ - - result = NULL; - - gstate = PyGILState_Ensure(); - - pytype = pygobject_lookup_class(type); - - if (pytype != NULL) - { - instance = PyObject_CallObject((PyObject *)pytype, NULL); - assert(instance != NULL); - - result = pygobject_get(instance); - - } - - PyGILState_Release(gstate); - - return result; - -} - -#endif - - - - /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ /* ---------------------------------------------------------------------------------- */ @@ -452,16 +390,53 @@ static bool g_pychrysalide_plugin_enable(GPyChrysalidePlugin *plugin) static bool g_pychrysalide_plugin_disable(GPyChrysalidePlugin *plugin) { + bool result; /* Bilan à retourner */ + bool standalone; /* Nature du chargement */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - gstate = PyGILState_Ensure(); + result = true; + + /** + * Le champ plugin->py_module n'est défini que via la fonction + * g_pychrysalide_plugin_enable(), qui n'est pas sollicitée lorsque + * le module PyChrysalide est mis en place directement par Python. + * + * L'analyse de ce champ pour retrouver la situation courante est + * plus fiable que celle du champ _standalone, potentiellement + * cohérent dans la version UI du greffon et resté à son état + * initial ici. + */ + + standalone = (plugin->py_module == NULL); + + /** + * Si on se trouve embarqué dans un interpréteur Python, le déchargement + * des greffons est organisé à partir de la fonction PyExit_pychrysalide(), + * directement appelée depuis un contexte Python. + * + * Un verrou n'est alors pas souhaité ici : + * + * python3d: ../Python/pystate.c:1687: PyGILState_Ensure: Assertion `gilstate->autoInterpreterState' failed. + * + * Avec : + * + * $ python3d --version + * Python 3.11.2 + * + */ + + if (!standalone) + gstate = PyGILState_Ensure(); clear_all_accesses_to_python_modules(); Py_XDECREF(plugin->py_module); plugin->py_module = NULL; - PyGILState_Release(gstate); + if (!standalone) + PyGILState_Release(gstate); + + return result; } @@ -782,6 +757,7 @@ PyMODINIT_FUNC PyInit_pychrysalide(void) details.standalone = _standalone; details.populate_extra = NULL; + details.create_self = g_pychrysalide_plugin_new; result = init_python_pychrysalide_module(&details); diff --git a/plugins/pychrysalide/format/executable.c b/plugins/pychrysalide/format/executable.c index 7d05578..f0d3d6b 100644 --- a/plugins/pychrysalide/format/executable.c +++ b/plugins/pychrysalide/format/executable.c @@ -47,9 +47,9 @@ /* Initialise la classe des formats exécutables. */ -static void py_executable_format_init_gclass(GExecutableFormatClass *, gpointer); +static int py_executable_format_init_gclass(GExecutableFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT, py_executable_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(executable_format, G_TYPE_EXECUTABLE_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_executable_format_init(PyObject *, PyObject *, PyObject *); @@ -95,23 +95,25 @@ static PyObject *py_executable_format_get_portions(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats exécutables. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_executable_format_init_gclass(GExecutableFormatClass *class, gpointer unused) +static int py_executable_format_init_gclass(GExecutableFormatClass *gclass, PyTypeObject *pyclass) { - class->get_machine = py_executable_format_get_target_machine_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_machine, py_executable_format_get_target_machine_wrapper); - class->get_main_addr = py_executable_format_get_main_address_wrapper; - class->refine_portions = py_executable_format_refine_portions_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_main_addr, py_executable_format_get_main_address_wrapper); + PY_CLASS_SET_WRAPPER(gclass->refine_portions, py_executable_format_refine_portions_wrapper); + + return 0; } @@ -784,6 +786,8 @@ bool ensure_python_executable_format_is_registered(void) if (!ensure_python_program_format_is_registered()) return false; + pyg_register_class_init(G_TYPE_EXECUTABLE_FORMAT, (PyGClassInitFunc)py_executable_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_EXECUTABLE_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/format/known.c b/plugins/pychrysalide/format/known.c index 5df2a8f..856c087 100644 --- a/plugins/pychrysalide/format/known.c +++ b/plugins/pychrysalide/format/known.c @@ -42,9 +42,9 @@ /* Initialise la classe des descriptions de fichier binaire. */ -static void py_known_format_init_gclass(GKnownFormatClass *, gpointer); +static int py_known_format_init_gclass(GKnownFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT, py_known_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(known_format, G_TYPE_KNOWN_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_known_format_init(PyObject *, PyObject *, PyObject *); @@ -84,23 +84,25 @@ static PyObject *py_known_format_get_content(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats connus. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_known_format_init_gclass(GKnownFormatClass *class, gpointer unused) +static int py_known_format_init_gclass(GKnownFormatClass *gclass, PyTypeObject *pyclass) { - class->get_key = py_known_format_get_key_wrapper; - class->get_desc = py_known_format_get_description_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_key, py_known_format_get_key_wrapper); + PY_CLASS_SET_WRAPPER(gclass->get_desc, py_known_format_get_description_wrapper); - class->analyze = py_known_format_analyze_wrapper; + PY_CLASS_SET_WRAPPER(gclass->analyze, py_known_format_analyze_wrapper); + + return 0; } @@ -635,6 +637,8 @@ bool ensure_python_known_format_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_KNOWN_FORMAT, (PyGClassInitFunc)py_known_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_KNOWN_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/format/program.c b/plugins/pychrysalide/format/program.c index 01b9703..57a359a 100644 --- a/plugins/pychrysalide/format/program.c +++ b/plugins/pychrysalide/format/program.c @@ -55,9 +55,9 @@ /* Initialise la classe des formats de programmes. */ -static void py_program_format_init_gclass(GProgramFormatClass *, gpointer); +static int py_program_format_init_gclass(GProgramFormatClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT, py_program_format_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(program_format, G_TYPE_PROGRAM_FORMAT); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_program_format_init(PyObject *, PyObject *, PyObject *); @@ -140,21 +140,23 @@ static PyObject *py_program_format_get_errors(PyObject *, void *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des formats de programmes. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_program_format_init_gclass(GProgramFormatClass *class, gpointer unused) +static int py_program_format_init_gclass(GProgramFormatClass *gclass, PyTypeObject *pyclass) { - class->get_endian = py_program_format_get_endianness_wrapper; - class->find_range_by_name = py_program_format_find_section_range_by_name_wrapper; + PY_CLASS_SET_WRAPPER(gclass->get_endian, py_program_format_get_endianness_wrapper); + PY_CLASS_SET_WRAPPER(gclass->find_range_by_name, py_program_format_find_section_range_by_name_wrapper); + + return 0; } @@ -1273,6 +1275,8 @@ bool ensure_python_program_format_is_registered(void) if (!ensure_python_known_format_is_registered()) return false; + pyg_register_class_init(G_TYPE_PROGRAM_FORMAT, (PyGClassInitFunc)py_program_format_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_PROGRAM_FORMAT, type)) return false; diff --git a/plugins/pychrysalide/glibext/Makefile.am b/plugins/pychrysalide/glibext/Makefile.am index 007ceee..2d45244 100644 --- a/plugins/pychrysalide/glibext/Makefile.am +++ b/plugins/pychrysalide/glibext/Makefile.am @@ -5,7 +5,6 @@ noinst_LTLIBRARIES = libpychrysaglibext.la # binarycursor.h binarycursor.c \ # buffercache.h buffercache.c \ # bufferline.h bufferline.c \ -# comparison.h comparison.c \ # configuration.h configuration.c \ # linecursor.h linecursor.c \ # linegen.h linegen.c \ @@ -21,7 +20,9 @@ noinst_LTLIBRARIES = libpychrysaglibext.la # endif libpychrysaglibext_la_SOURCES = \ + comparable.h comparable.c \ constants.h constants.c \ + hashable.h hashable.c \ module.h module.c \ objhole.h objhole.c \ portion.h portion.c \ diff --git a/plugins/pychrysalide/glibext/comparable.c b/plugins/pychrysalide/glibext/comparable.c new file mode 100644 index 0000000..e4982d7 --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.c @@ -0,0 +1,482 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - équivalent Python du fichier "glibext/comparable.c" + * + * Copyright (C) 2025 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 "comparable.h" + + +#include <assert.h> +#include <pygobject.h> + + +#include <glibext/comparable-int.h> + + +#include "../access.h" +#include "../helpers.h" + + + +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_comparable_object_interface_init(GComparableObjectInterface *, gpointer *); + +/* Réalise une comparaison étendue entre objets. */ +static int py_comparable_object_compare_wrapper(const GComparableObject *, const GComparableObject *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet le statut d'une comparaison effectuée par le parent. */ +static PyObject *py_comparable_object_parent_compare(PyObject *, PyObject *); + +/* Effectue une comparaison avec un objet 'ComparableObject'. */ +static PyObject *py_comparable_object_richcompare(PyObject *, PyObject *, int); + + + +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* unused = adresse non utilisée ici. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void py_comparable_object_interface_init(GComparableObjectInterface *iface, gpointer *unused) +{ +#define COMPARABLE_OBJECT_DOC \ + "The ComparableObject class provides an interface to compare" \ + " objects.\n" \ + "\n" \ + "A typical class declaration for a new implementation looks like:\n" \ + "\n" \ + " class NewImplem(GObject.Object, ComparableObject):\n" \ + " ...\n" \ + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.ComparableObject._compare().\n" + + iface->compare = py_comparable_object_compare_wrapper; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static int py_comparable_object_compare_wrapper(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *args; /* Arguments pour l'appel */ + PyObject *pyret; /* Bilan de consultation */ + +#define COMPARABLE_OBJECT_COMPARE_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _compare, "$self, other", \ + METH_VARARGS, \ + "Abstract method allowing to compare two objects implementing" \ + " the interface. This method is used to handle rich comparisons"\ + " automatically.\n" \ + "\n" \ + "The result has to be an integer lesser than, equal to, or" \ + " greater than zero if *self* is found, respectively, to be" \ + " lesser than, to match, or to be greater than *other*.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; + + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(object)); + + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); + + pyret = run_python_method(pyobj, "_compare", args); + + if (pyret != NULL) + { + if (PyLong_Check(pyret)) + result = PyLong_AsLong(pyret); + + else + PyErr_SetString(PyExc_TypeError, _("comparison status has to be a signed integer")); + + } + + Py_XDECREF(pyret); + + Py_DECREF(args); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); + + return result; + +} + + + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet le statut d'une comparaison effectuée par le parent.* +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_parent_compare(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + GComparableObject *other; /* Second objet à comparer */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *object; /* Mécanismes natifs */ + GComparableObjectInterface *iface; /* Interface utilisée */ + GComparableObjectInterface *parent_iface; /* Interface parente */ + int status; /* Bilan d'une comparaison */ + +#define COMPARABLE_OBJECT_PARENT_COMPARE_METHOD PYTHON_METHOD_DEF \ +( \ + parent_compare, "$sel, otherf", \ + METH_VARARGS, py_comparable_object, \ + "Provide the comparison status defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a signed integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.ComparableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._compare() function has to be" \ + " used instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + ret = PyArg_ParseTuple(args, "O&", convert_to_comparable_object, &other); + if (!ret) return NULL; + + object = G_COMPARABLE_OBJECT(pygobject_get(self)); + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the ComparableObject interface")); + + result = NULL; + + } + else + { + status = parent_iface->compare(object, other); + + result = PyLong_FromLong(status); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : a = premier object Python à consulter. * +* b = second object Python à consulter. * +* op = type de comparaison menée. * +* * +* Description : Effectue une comparaison avec un objet 'ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_comparable_object_richcompare(PyObject *a, PyObject *b, int op) +{ + PyObject *result; /* Bilan à retourner */ + int ret; /* Bilan de lecture des args. */ + GComparableObject *obj_a; /* Instance à manipuler #1 */ + GComparableObject *obj_b; /* Instance à manipuler #2 */ + int status; /* Bilan d'une comparaison */ + + ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_object_type()); + if (!ret) + { + result = Py_NotImplemented; + goto cmp_done; + } + + obj_a = G_COMPARABLE_OBJECT(pygobject_get(a)); + obj_b = G_COMPARABLE_OBJECT(pygobject_get(b)); + + status = g_comparable_object_compare(obj_a, obj_b); + + 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: + assert(false); + result = Py_NotImplemented; + break; + + } + + cmp_done: + + Py_INCREF(result); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + return result; + +} + + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Fournit un accès à une définition de type à diffuser. * +* * +* Retour : Définition d'objet pour Python. * +* * +* Remarques : - * +* * +******************************************************************************/ + +PyTypeObject *get_python_comparable_object_type(void) +{ + static PyMethodDef py_comparable_object_methods[] = { + COMPARABLE_OBJECT_COMPARE_WRAPPER, + COMPARABLE_OBJECT_PARENT_COMPARE_METHOD, + { NULL } + }; + + static PyGetSetDef py_comparable_object_getseters[] = { + { NULL } + }; + + static PyTypeObject py_comparable_object_type = { + + PyVarObject_HEAD_INIT(NULL, 0) + + .tp_name = "pychrysalide.glibext.ComparableObject", + .tp_basicsize = sizeof(PyObject), + + /** + * Une valeur de .tp_richcompare non nulle écarte la définition du + * champ .tp_hash à la valeur par défaut du type PyBaseObject_Type + * dans les préparatifs de la fonction Python inherit_slots(). + * + * Ces préparatifs se poursuivent avec type_ready_set_hash(), + * qui initialise .tp_hash avec PyObject_HashNotImplemented(), + * qui n'est donc pas un comportement par défaut. + * + * Côté PyGObject, la fonction pygobject_inherit_slots() y voit + * une implémentation de .tp_hash personnalisée, ce qui bloque + * la défintion d'autres personnalisations, comme celle de + * l'interface HashableObject. + * + * La valeur nominale nulle est ainsi écartée au préalable ici. + */ + .tp_hash = (hashfunc)_Py_HashPointer, + + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + + .tp_doc = COMPARABLE_OBJECT_DOC, + + .tp_richcompare = py_comparable_object_richcompare, + + .tp_methods = py_comparable_object_methods, + .tp_getset = py_comparable_object_getseters + + }; + + return &py_comparable_object_type; + +} + + +/****************************************************************************** +* * +* Paramètres : - * +* * +* Description : Prend en charge l'objet 'pychrysalide.....ComparableObject'. * +* * +* Retour : Bilan de l'opération. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool ensure_python_comparable_object_is_registered(void) +{ + PyTypeObject *type; /* Type 'ComparableObject' */ + PyObject *module; /* Module à recompléter */ + PyObject *dict; /* Dictionnaire du module */ + + static GInterfaceInfo info = { /* Paramètres d'inscription */ + + .interface_init = (GInterfaceInitFunc)py_comparable_object_interface_init, + .interface_finalize = NULL, + .interface_data = NULL, + + }; + + type = get_python_comparable_object_type(); + + if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) + { + module = get_access_to_python_module("pychrysalide.glibext"); + + dict = PyModule_GetDict(module); + + if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_OBJECT, type, &info)) + return false; + + } + + return true; + +} + + +/****************************************************************************** +* * +* 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 interface d'objet comparable. * +* * +* Retour : Bilan de l'opération, voire indications supplémentaires. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int convert_to_comparable_object(PyObject *arg, void *dst) +{ + int result; /* Bilan à retourner */ + + result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_object_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 comparable object"); + break; + + case 1: + *((GComparableObject **)dst) = G_COMPARABLE_OBJECT(pygobject_get(arg)); + break; + + default: + assert(false); + break; + + } + + return result; + +} diff --git a/plugins/pychrysalide/glibext/comparable.h b/plugins/pychrysalide/glibext/comparable.h new file mode 100644 index 0000000..d4c6ecf --- /dev/null +++ b/plugins/pychrysalide/glibext/comparable.h @@ -0,0 +1,45 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour l'équivalent Python du fichier "glibext/comparable.h" + * + * Copyright (C) 2025 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_GLIBEXT_COMPARABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H + + +#include <Python.h> +#include <stdbool.h> + + + +/* Fournit un accès à une définition de type à diffuser. */ +PyTypeObject *get_python_comparable_object_type(void); + +/* Prend en charge l'objet 'pychrysalide.glibext.ComparableObject'. */ +bool ensure_python_comparable_object_is_registered(void); + +/* Tente de convertir en interface d'objet comparable. */ +int convert_to_comparable_object(PyObject *, void *); + + + +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARABLE_H */ diff --git a/plugins/pychrysalide/glibext/comparison.c b/plugins/pychrysalide/glibext/hashable.c index 548f700..c870d55 100644 --- a/plugins/pychrysalide/glibext/comparison.c +++ b/plugins/pychrysalide/glibext/hashable.c @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - équivalent Python du fichier "glibext/comparison.h" + * hashable.c - équivalent Python du fichier "glibext/hashable.c" * - * Copyright (C) 2018-2019 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,38 +22,40 @@ */ -#include "comparison.h" +#include "hashable.h" +#include <assert.h> #include <pygobject.h> -#include <glibext/comparison-int.h> +#include <glibext/hashable-int.h> -#include "constants.h" #include "../access.h" #include "../helpers.h" -#include "../analysis/content.h" /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -/* Procède à l'initialisation de l'interface de comparaison. */ -static void py_comparable_item_interface_init(GComparableItemIface *, gpointer *); +/* Procède à l'initialisation de l'interface de détermination. */ +static void py_hashable_object_interface_init(GHashableObjectInterface *, gpointer *); -/* Réalise une comparaison entre objets selon un critère précis. */ -static bool py_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static guint py_hashable_object_hash_wrapper(const GHashableObject *); /* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ -/* Effectue une comparaison avec un objet 'ComparableItem'. */ -static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); +/* Transmet l'empreinte d'un objet calculée par son parent. */ +static PyObject *py_hashable_object_parent_hash(PyObject *, PyObject *); + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +static Py_hash_t py_hashable_object_hash(PyObject *); @@ -67,7 +69,7 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); * Paramètres : iface = interface GLib à initialiser. * * unused = adresse non utilisée ici. * * * -* Description : Procède à l'initialisation de l'interface de comparaison. * +* Description : Procède à l'initialisation de l'interface de détermination. * * * * Retour : - * * * @@ -75,72 +77,78 @@ static PyObject *py_comparable_item_richcompare(PyObject *, PyObject *, int); * * ******************************************************************************/ -static void py_comparable_item_interface_init(GComparableItemIface *iface, gpointer *unused) +static void py_hashable_object_interface_init(GHashableObjectInterface *iface, gpointer *unused) { - -#define COMPARABLE_ITEM_DOC \ - "ComparableItem provides an interface to compare objects.\n" \ +#define HASHABLE_OBJECT_DOC \ + "The HashableObject class defines a interface ensuring that a" \ + " customized hashing method is available for an object.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, ComparableItem):\n" \ + " class NewImplem(GObject.Object, HashableObject):\n" \ " ...\n" \ - "\n" + "\n" \ + "The following method has to be defined for new implementations:\n" \ + "* pychrysalide.glibext.HashableObject._hash().\n" - iface->cmp_rich = py_comparable_item_compare_rich; + iface->hash = py_hashable_object_hash_wrapper; } /****************************************************************************** * * -* Paramètres : item = premier objet à cnsulter pour une comparaison. * -* other = second objet à cnsulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * +* Paramètres : object = objet dont l'instance est à consulter. * * * -* Description : Réalise une comparaison entre objets selon un critère précis.* +* Description : Calcule l'empreinte sur 32 bits d'un objet. * * * -* Retour : true si la comparaison a pu être effectuée, false sinon. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static bool py_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) +static guint py_hashable_object_hash_wrapper(const GHashableObject *object) { - bool result; /* Etat à retourner */ + guint result; /* Valeur à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ - PyObject *pyitem; /* Objet Python concerné #1 */ - PyObject *pyother; /* Objet Python concerné #2 */ + PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ - int ret; /* Bilan d'une conversion */ - result = false; +#define HASHABLE_OBJECT_HASH_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _hash, "$self", \ + METH_NOARGS, \ + "Abstract method computing a hash from an object which is used" \ + " as the default implementation of the __hash__() method.\n" \ + "\n" \ + "The result has to be an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " an integer." \ +) + + result = 0; gstate = PyGILState_Ensure(); - pyitem = pygobject_new(G_OBJECT(item)); - pyother = pygobject_new(G_OBJECT(other)); + pyobj = pygobject_new(G_OBJECT(object)); - pyret = PyObject_RichCompare(pyitem, pyother, op); + pyret = run_python_method(pyobj, "_hash", NULL); if (pyret != NULL) { - ret = PyBool_Check(pyret); + if (PyLong_Check(pyret)) + result = PyLong_AsUnsignedLong(pyret); - if (ret) - { - *status = (pyret == Py_True); - result = true; - } - - Py_DECREF(pyret); + else + PyErr_SetString(PyExc_TypeError, _("computed hash value has to be an unsigned integer")); } - Py_DECREF(pyother); - Py_DECREF(pyitem); + Py_XDECREF(pyret); + + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -157,47 +165,97 @@ static bool py_comparable_item_compare_rich(const GComparableItem *item, const G /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * * * -* Description : Effectue une comparaison avec un objet 'ComparableItem'. * +* Description : Transmet l'empreinte d'un objet calculée par son parent. * * * -* Retour : Bilan de l'opération. * +* Retour : Valeur de représentation, unique pour l'objet ou non. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op) +static PyObject *py_hashable_object_parent_hash(PyObject *self, PyObject *args) { - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GComparableItem *item_a; /* Instance à manipuler #1 */ - GComparableItem *item_b; /* Instance à manipuler #2 */ - bool valid; /* Indication de validité */ - bool status; /* Résultat d'une comparaison */ - - ret = PyObject_IsInstance(b, (PyObject *)get_python_comparable_item_type()); - if (!ret) + PyObject *result; /* Valeur à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + GHashableObjectInterface *iface; /* Interface utilisée */ + GHashableObjectInterface *parent_iface; /* Interface parente */ + guint hash; /* Valeur d'empreitne */ + +#define HASHABLE_OBJECT_PARENT_HASH_METHOD PYTHON_METHOD_DEF \ +( \ + parent_hash, "$self", \ + METH_NOARGS, py_hashable_object, \ + "Provide the hash value defined by the interface implementation"\ + " from the object native parent.\n" \ + "\n" \ + "The result is an unsigned integer.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.HashableObject" \ + " interface.\n" \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._hash() function has to be used" \ + " instead." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) { - result = Py_NotImplemented; - goto cmp_done; + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the HashableObject interface")); + + result = NULL; + } + else + { + hash = parent_iface->hash(object); - item_a = G_COMPARABLE_ITEM(pygobject_get(a)); - item_b = G_COMPARABLE_ITEM(pygobject_get(b)); + result = PyLong_FromUnsignedLong(hash); - valid = g_comparable_item_compare_rich(item_a, item_b, op, &status); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} - if (valid) - result = status ? Py_True : Py_False; - else - result = Py_NotImplemented; - cmp_done: +/****************************************************************************** +* * +* Paramètres : self = objet manipulé ici. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ - Py_INCREF(result); +static Py_hash_t py_hashable_object_hash(PyObject *self) +{ + Py_hash_t result; /* Empreinte à retourner */ + GHashableObject *object; /* Mécanismes natifs */ + + object = G_HASHABLE_OBJECT(pygobject_get(self)); + + result = g_hashable_object_hash(object); + + UPDATE_RESULT_IF_RAISED_EXCEPTION(-1); return result; @@ -216,44 +274,46 @@ static PyObject *py_comparable_item_richcompare(PyObject *a, PyObject *b, int op * * ******************************************************************************/ -PyTypeObject *get_python_comparable_item_type(void) +PyTypeObject *get_python_hashable_object_type(void) { - static PyMethodDef py_comparable_item_methods[] = { + static PyMethodDef py_hashable_object_methods[] = { + HASHABLE_OBJECT_HASH_WRAPPER, + HASHABLE_OBJECT_PARENT_HASH_METHOD, { NULL } }; - static PyGetSetDef py_comparable_item_getseters[] = { + static PyGetSetDef py_hashable_object_getseters[] = { { NULL } }; - static PyTypeObject py_comparable_item_type = { + static PyTypeObject py_hashable_object_type = { PyVarObject_HEAD_INIT(NULL, 0) - .tp_name = "pychrysalide.glibext.ComparableItem", + .tp_name = "pychrysalide.glibext.HashableObject", .tp_basicsize = sizeof(PyObject), - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, + .tp_hash = py_hashable_object_hash, - .tp_doc = COMPARABLE_ITEM_DOC, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, - .tp_richcompare = py_comparable_item_richcompare, + .tp_doc = HASHABLE_OBJECT_DOC, - .tp_methods = py_comparable_item_methods, - .tp_getset = py_comparable_item_getseters, + .tp_methods = py_hashable_object_methods, + .tp_getset = py_hashable_object_getseters, }; - return &py_comparable_item_type; + return &py_hashable_object_type; } /****************************************************************************** * * -* Paramètres : module = module dont la définition est à compléter. * +* Paramètres : - * * * -* Description : Prend en charge l'objet 'pychrysalide.....ComparableItem'. * +* Description : Prend en charge l'objet 'pychrysalide.....HashableObject'. * * * * Retour : Bilan de l'opération. * * * @@ -261,21 +321,21 @@ PyTypeObject *get_python_comparable_item_type(void) * * ******************************************************************************/ -bool ensure_python_comparable_item_is_registered(void) +bool ensure_python_hashable_object_is_registered(void) { - PyTypeObject *type; /* Type Python 'ComparableItem' */ + PyTypeObject *type; /* Type Python 'HashableObject'*/ PyObject *module; /* Module à recompléter */ PyObject *dict; /* Dictionnaire du module */ static GInterfaceInfo info = { /* Paramètres d'inscription */ - .interface_init = (GInterfaceInitFunc)py_comparable_item_interface_init, + .interface_init = (GInterfaceInitFunc)py_hashable_object_interface_init, .interface_finalize = NULL, .interface_data = NULL, }; - type = get_python_comparable_item_type(); + type = get_python_hashable_object_type(); if (!PyType_HasFeature(type, Py_TPFLAGS_READY)) { @@ -283,10 +343,7 @@ bool ensure_python_comparable_item_is_registered(void) dict = PyModule_GetDict(module); - if (!register_interface_for_pygobject(dict, G_TYPE_COMPARABLE_ITEM, type, &info)) - return false; - - if (!define_comparable_item_constants(type)) + if (!register_interface_for_pygobject(dict, G_TYPE_HASHABLE_OBJECT, type, &info)) return false; } @@ -301,7 +358,7 @@ bool ensure_python_comparable_item_is_registered(void) * 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 élément comparable. * +* Description : Tente de convertir en interface d'objet réductible. * * * * Retour : Bilan de l'opération, voire indications supplémentaires. * * * @@ -309,11 +366,11 @@ bool ensure_python_comparable_item_is_registered(void) * * ******************************************************************************/ -int convert_to_comparable_item(PyObject *arg, void *dst) +int convert_to_hashable_object(PyObject *arg, void *dst) { int result; /* Bilan à retourner */ - result = PyObject_IsInstance(arg, (PyObject *)get_python_comparable_item_type()); + result = PyObject_IsInstance(arg, (PyObject *)get_python_hashable_object_type()); switch (result) { @@ -323,11 +380,11 @@ int convert_to_comparable_item(PyObject *arg, void *dst) break; case 0: - PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to comparable item"); + PyErr_SetString(PyExc_TypeError, "unable to convert the provided argument to hashable object"); break; case 1: - *((GComparableItem **)dst) = G_COMPARABLE_ITEM(pygobject_get(arg)); + *((GHashableObject **)dst) = G_HASHABLE_OBJECT(pygobject_get(arg)); break; default: diff --git a/plugins/pychrysalide/glibext/comparison.h b/plugins/pychrysalide/glibext/hashable.h index 79f7092..8583118 100644 --- a/plugins/pychrysalide/glibext/comparison.h +++ b/plugins/pychrysalide/glibext/hashable.h @@ -1,8 +1,8 @@ /* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour l'équivalent Python du fichier "glibext/comparison.h" + * hashable.h - prototypes pour l'équivalent Python du fichier "glibext/hashable.h" * - * Copyright (C) 2018 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -22,8 +22,8 @@ */ -#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H -#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H +#ifndef _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H +#define _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H #include <Python.h> @@ -32,14 +32,14 @@ /* Fournit un accès à une définition de type à diffuser. */ -PyTypeObject *get_python_comparable_item_type(void); +PyTypeObject *get_python_hashable_object_type(void); -/* Prend en charge l'objet 'pychrysalide.glibext.ComparableItem'. */ -bool ensure_python_comparable_item_is_registered(void); +/* Prend en charge l'objet 'pychrysalide.glibext.HashableObject'. */ +bool ensure_python_hashable_object_is_registered(void); -/* Tente de convertir en élément comparable. */ -int convert_to_comparable_item(PyObject *, void *); +/* Tente de convertir en interface d'objet réductible. */ +int convert_to_hashable_object(PyObject *, void *); -#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_COMPARISON_H */ +#endif /* _PLUGINS_PYCHRYSALIDE_GLIBEXT_HASHABLE_H */ diff --git a/plugins/pychrysalide/glibext/module.c b/plugins/pychrysalide/glibext/module.c index 6cca246..8adae07 100644 --- a/plugins/pychrysalide/glibext/module.c +++ b/plugins/pychrysalide/glibext/module.c @@ -33,13 +33,14 @@ #include "buffercache.h" #include "bufferline.h" #include "bufferview.h" -#include "comparison.h" #include "configuration.h" #include "linecursor.h" #include "linegen.h" #include "loadedpanel.h" #include "named.h" */ +#include "comparable.h" +#include "hashable.h" #include "objhole.h" #include "portion.h" #include "singleton.h" @@ -112,6 +113,8 @@ bool populate_glibext_module(void) result = true; + if (result) result = ensure_python_comparable_object_is_registered(); + if (result) result = ensure_python_hashable_object_is_registered(); if (result) result = ensure_python_singleton_candidate_is_registered(); if (result) result = ensure_python_string_builder_is_registered(); @@ -129,7 +132,6 @@ bool populate_glibext_module(void) #ifdef INCLUDE_GTK_SUPPORT if (result) result = ensure_python_buffer_view_is_registered(); #endif - if (result) result = ensure_python_comparable_item_is_registered(); if (result) result = ensure_python_config_param_is_registered(); if (result) result = ensure_python_config_param_iterator_is_registered(); if (result) result = ensure_python_generic_config_is_registered(); diff --git a/plugins/pychrysalide/glibext/singleton.c b/plugins/pychrysalide/glibext/singleton.c index ca847de..8712506 100644 --- a/plugins/pychrysalide/glibext/singleton.c +++ b/plugins/pychrysalide/glibext/singleton.c @@ -49,20 +49,20 @@ static GSingletonCandidate **py_singleton_candidate_list_inner_instances_wrapper /* Met à jour une liste de candidats embarqués par un candidat. */ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *); +/* Marque un candidat comme figé. */ +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *); -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *, const GSingletonCandidate *); +/* Indique si le candidat est figé. */ +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -static PyObject *py_singleton_candidate_hash(PyObject *, PyObject *); +/* Indique si le candidat est figé. */ +static PyObject *py_singleton_candidate_is_read_only(PyObject *, void *); -/* Fournit une liste de candidats embarqués par un candidat. */ -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *, void *); +/* Crée une copie modifiable d'un object unique. */ +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *); -/* Effectue une comparaison avec un objet 'SingletonCandidate'. */ -static PyObject *py_singleton_candidate_richcompare(PyObject *, PyObject *, int); +/* Crée une copie modifiable d'un object unique. */ +static PyObject *py_singleton_candidate_dup(PyObject *, PyObject *); @@ -104,25 +104,37 @@ static void py_singleton_candidate_interface_init(GSingletonCandidateInterface * " aiming at becoming singleton instances. All shared singletons are" \ " registered within a pychrysalide.glibext.SingletonFactory object.\n" \ "\n" \ + "Implementations of the pychrysalide.glibext.HashableObject and" \ + " pychrysalide.glibext.ComparableObject interfaces are required for" \ + " types implementing the SingletonCandidate interface.\n" \ + "\n" \ "The main implemantations come with types derived from" \ - " pychrysalide.analysis.DataType.\n" \ + " pychrysalide.analysis.DataType (with possible recursivity) or from" \ + " pychrysalide.arch.ArchOperand.\n" \ "\n" \ "A typical class declaration for a new implementation looks like:\n" \ "\n" \ - " class NewImplem(GObject.Object, SingletonCandidate):\n" \ + " class NewImplem(GObject.Object, HashableObject, ComparableObject," \ + " SingletonCandidate):\n" \ " ...\n" \ "\n" \ "The following methods have to be defined for new implementations:\n" \ + "* pychrysalide.glibext.SingletonCandidate._mark_as_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._is_read_only();\n" \ + "* pychrysalide.glibext.SingletonCandidate._dup().\n" \ + "\n" \ + "The following methods may bbe defined for new implementations if" \ + " inner SingletonCandidate objets are carried:\n" \ "* pychrysalide.glibext.SingletonCandidate._list_inner_instances();\n" \ - "* pychrysalide.glibext.SingletonCandidate._update_inner_instances();\n"\ - "* pychrysalide.glibext.SingletonCandidate.__hash__();\n" \ - "* pychrysalide.glibext.SingletonCandidate.__eq__().\n" + "* pychrysalide.glibext.SingletonCandidate._update_inner_instances().\n" iface->update_inner = py_singleton_candidate_update_inner_instances_wrapper; iface->list_inner = py_singleton_candidate_list_inner_instances_wrapper; - iface->hash = py_singleton_candidate___hash__wrapper; - iface->is_equal = py_singleton_candidate___eq__wrapper; + iface->mark_as_ro = py_singleton_candidate_mark_as_read_only_wrapper; + iface->is_ro = py_singleton_candidate_is_read_only_wrapper; + + iface->dup = py_singleton_candidate_dup_wrapper; } @@ -297,122 +309,97 @@ static void py_singleton_candidate_update_inner_instances_wrapper(GSingletonCand * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -static guint py_singleton_candidate___hash__wrapper(const GSingletonCandidate *candidate) +static void py_singleton_candidate_mark_as_read_only_wrapper(GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_HASH_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __hash__, "$self, /", \ - METH_NOARGS, \ - "Abstract method used to produce a hash of the object.\n" \ - "\n" \ - "The result must be an integer value up to 64 bits." \ - "\n" \ - "Inner instances which are listed through the" \ - " pychrysalide.glibext.SingletonCandidate._list_inner_instances()" \ - " method do not need to get processed here as they are handled" \ - " automatically by the interface core." \ +#define SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _mark_as_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to seal the object as unmodifiable.\n" \ + "\n" \ + "No result is expected." \ ) - result = 0; - gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__hash__")) - { - pyret = run_python_method(pyobj, "__hash__", NULL); + pyret = run_python_method(pyobj, "_mark_as_read_only", NULL); - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLongMask(pyret); - - Py_DECREF(pyret); - - } - - } + Py_XDECREF(pyret); Py_DECREF(pyobj); PyGILState_Release(gstate); - return result; - } /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate *candidate, const GSingletonCandidate *other) +static bool py_singleton_candidate_is_read_only_wrapper(const GSingletonCandidate *candidate) { - guint result; /* Empreinte à retourner */ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ - PyObject *args; /* Arguments pour l'appel */ PyObject *pyret; /* Bilan de consultation */ -#define SINGLETON_CANDIDATE_EQ_WRAPPER PYTHON_WRAPPER_DEF \ -( \ - __eq__, "$self, other, /", \ - METH_NOARGS, \ - "Abstract method used to provide the *__eq__* method for" \ - " rich comparison.\n" \ - "\n" \ - "The expected result is a boolean value." \ +#define SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _is_read_only, "$self", \ + METH_NOARGS, \ + "Abstract method used to provide the state of the object: are" \ + " its properties frozen (*True*) or can it be modified" \ + " (*False*)?\n" \ + "\n" \ + "The result has to be a boolean status.\n" \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a boolean." \ ) - result = 0; + result = false; gstate = PyGILState_Ensure(); pyobj = pygobject_new(G_OBJECT(candidate)); - if (has_python_method(pyobj, "__eq__")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, pygobject_new(G_OBJECT(other))); - - pyret = run_python_method(pyobj, "__eq__", args); - - if (pyret != NULL) - { - if (PyLong_Check(pyret)) - result = PyLong_AsUnsignedLong(pyret); + pyret = run_python_method(pyobj, "_is_read_only", NULL); - Py_DECREF(pyret); + if (pyret != NULL) + { + if (PyBool_Check(pyret)) + result = (pyret == Py_True); - } - - Py_DECREF(args); + else + PyErr_SetString(PyExc_TypeError, _("status has to be provided as a boolean value")); } + Py_XDECREF(pyret); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -424,47 +411,77 @@ static gboolean py_singleton_candidate___eq__wrapper(const GSingletonCandidate * /****************************************************************************** * * -* Paramètres : self = objet dont l'instance se veut unique. * -* args = adresse non utilisée ici. * +* Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) +static GSingletonCandidate *py_singleton_candidate_dup_wrapper(const GSingletonCandidate *candidate) { - PyObject *result; /* Emplacement à retourner */ - GSingletonCandidate *candidate; /* Mécanismes natifs */ - guint hash; /* Valeur d'empreitne */ + GSingletonCandidate *result; /* Instance à retourner */ + PyGILState_STATE gstate; /* Sauvegarde d'environnement */ + PyObject *pyobj; /* Objet Python concerné */ + PyObject *pyret; /* Bilan de consultation */ + PyObject *state; /* Validation du mode */ -#define SINGLETON_CANDIDATE_HASH_METHOD PYTHON_METHOD_DEF \ -( \ - hash, "$self", \ - METH_NOARGS, py_singleton_candidate, \ - "Compute the hash value of the singleton candidate.\n" \ - "\n" \ - "The method relies on the interface core to include in the" \ - " process the optional embedded instances which may become" \ - " singletons.\n" \ - "\n" \ - "The result is an integer value.\n" \ - "\n" \ - "Even if the Python *hash()* method, relying on the" \ - " pychrysalide.glibext.SingletonCandidate.__hash__()" \ - " implementation, provides values up to 64 bits, the final" \ - " hashes processed by the native GLib hash methods are" \ - " limited to 32 bits values." \ +#define SINGLETON_CANDIDATE_DUP_WRAPPER PYTHON_WRAPPER_DEF \ +( \ + _dup, "$self", \ + METH_NOARGS, \ + "Abstract method used to create a copy of the object. This" \ + " has to be able to get modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ + "\n" \ + "The result has to be a new intance of type(self).\n" \ + "\n" \ + "A *TypeError* exception is raised if the type of the" \ + " return value is different from the type of self.\n" \ + "\n" \ + "A *ValueError* exception is raised of the return object" \ + " is in read-only mode." \ ) - candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); + result = false; - hash = g_singleton_candidate_hash(candidate); + gstate = PyGILState_Ensure(); + + pyobj = pygobject_new(G_OBJECT(candidate)); + + pyret = run_python_method(pyobj, "_dup", NULL); + + if (pyret != NULL) + { + if (Py_TYPE(pyret) != Py_TYPE(pyobj)) + PyErr_SetString(PyExc_TypeError, _("the result type is different from the source type")); + + else + { + state = py_singleton_candidate_is_read_only(pyret, NULL); + + if (state != NULL) + { + if (state != Py_False) + PyErr_SetString(PyExc_ValueError, _("the result type can not be in read-only mode")); + + Py_DECREF(state); + + } - result = PyLong_FromUnsignedLong(hash); + } + + } + + Py_XDECREF(pyret); + + Py_DECREF(pyobj); + + PyGILState_Release(gstate); return result; @@ -473,48 +490,51 @@ static PyObject *py_singleton_candidate_hash(PyObject *self, PyObject *args) /****************************************************************************** * * -* Paramètres : self = objet Python concerné par l'appel. * -* closure = non utilisé ici. * +* Paramètres : self = objet manipulé ici. * +* args = adresse non utilisée ici. * * * -* Description : Fournit une liste de candidats embarqués par un candidat. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : Liste de candidats internes, vide si aucun. * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void *closure) +static PyObject *py_singleton_candidate_dup(PyObject *self, PyObject *args) { - PyObject *result; /* Valeur à retourner */ + PyObject *result; /* Emplacement à retourner */ GSingletonCandidate *candidate; /* Mécanismes natifs */ - size_t count; /* Quantité d'objets internes */ - GSingletonCandidate **instances; /* Liste des embarqués */ - size_t i; /* Boucle de parcours */ + GSingletonCandidate *copy; /* Copie des mécanismes natifs */ -#define SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB PYTHON_GET_DEF_FULL \ -( \ - inner_instances, py_singleton_candidate, \ - "List of optional internal singleton candidate instances.\n" \ - "\n" \ - "The result has to be a tuple containing zero or more" \ - " pychrysalide.glibext.SingletonCandidate instances." \ +#define SINGLETON_CANDIDATE_DUP_METHOD PYTHON_METHOD_DEF \ +( \ + dup, "$self", \ + METH_NOARGS, py_singleton_candidate, \ + "Create a copy of the object. This has to be able to get" \ + " modified (ie. its" \ + " pychrysalide.glibext.SingletonCandidate.read_only status" \ + " has to be *False*).\n" \ + "\n" \ + "The result has to be a new intance of type(self)." \ ) candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - instances = g_singleton_candidate_list_inner_instances(candidate, &count); + copy = g_singleton_candidate_dup(candidate); - result = PyTuple_New(count); + if (copy == NULL) + result = NULL; - for (i = 0; i < count; i++) + else { - PyTuple_SetItem(result, i, pygobject_new(G_OBJECT(instances[i]))); - g_object_unref(G_OBJECT(instances[i])); + result = pygobject_new(G_OBJECT(candidate)); + + unref_object(copy); + } - if (instances != NULL) - free(instances); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); return result; @@ -523,50 +543,39 @@ static PyObject *py_singleton_candidate_get_inner_instances(PyObject *self, void /****************************************************************************** * * -* Paramètres : a = premier object Python à consulter. * -* b = second object Python à consulter. * -* op = type de comparaison menée. * +* Paramètres : self = objet Python concerné par l'appel. * +* closure = non utilisé ici. * * * -* Description : Effectue une comparaison avec un objet 'SingletonCandidate'. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de l'opération. * +* Retour : True si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static PyObject *py_singleton_candidate_richcompare(PyObject *a, PyObject *b, int op) +static PyObject *py_singleton_candidate_is_read_only(PyObject *self, void *closure) { - PyObject *result; /* Bilan à retourner */ - int ret; /* Bilan de lecture des args. */ - GSingletonCandidate *cand_a; /* Premier élément à traiter */ - GSingletonCandidate *cand_b; /* Second élément à traiter */ - gboolean status; /* Résultat d'une comparaison */ - - if (op != Py_EQ) - { - result = Py_NotImplemented; - goto cmp_done; - } - - ret = PyObject_IsInstance(b, (PyObject *)get_python_singleton_candidate_type()); - if (!ret) - { - result = Py_NotImplemented; - goto cmp_done; - } - - cand_a = G_SINGLETON_CANDIDATE(pygobject_get(a)); - cand_b = G_SINGLETON_CANDIDATE(pygobject_get(b)); + PyObject *result; /* Valeur à retourner */ + GSingletonCandidate *candidate; /* Mécanismes natifs */ + bool state; /* Etat de l'objet courant */ - status = g_singleton_candidate_is_equal(cand_a, cand_b); +#define SINGLETON_CANDIDATE_READ_ONLY_ATTRIB PYTHON_IS_DEF_FULL \ +( \ + read_only, py_singleton_candidate, \ + "Boolean state of the object: *True* if all its properties are" \ + " frozen, *False* if the object can be modified." \ +) - result = (status ? Py_True : Py_False); + candidate = G_SINGLETON_CANDIDATE(pygobject_get(self)); - cmp_done: + state = g_singleton_candidate_is_read_only(candidate); + result = state ? Py_True : Py_False; Py_INCREF(result); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + return result; } @@ -589,14 +598,15 @@ PyTypeObject *get_python_singleton_candidate_type(void) static PyMethodDef py_singleton_candidate_methods[] = { SINGLETON_CANDIDATE_LIST_INNER_INSTANCES_WRAPPER, SINGLETON_CANDIDATE_UPDATE_INNER_INSTANCES_WRAPPER, - SINGLETON_CANDIDATE_HASH_WRAPPER, - SINGLETON_CANDIDATE_EQ_WRAPPER, - SINGLETON_CANDIDATE_HASH_METHOD, + SINGLETON_CANDIDATE_MARK_AS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_IS_READ_ONLY_WRAPPER, + SINGLETON_CANDIDATE_DUP_WRAPPER, + SINGLETON_CANDIDATE_DUP_METHOD, { NULL } }; static PyGetSetDef py_singleton_candidate_getseters[] = { - SINGLETON_CANDIDATE_INNER_INSTANCES_ATTRIB, + SINGLETON_CANDIDATE_READ_ONLY_ATTRIB, { NULL } }; @@ -611,8 +621,6 @@ PyTypeObject *get_python_singleton_candidate_type(void) .tp_doc = SINGLETON_CANDIDATE_DOC, - .tp_richcompare = py_singleton_candidate_richcompare, - .tp_methods = py_singleton_candidate_methods, .tp_getset = py_singleton_candidate_getseters diff --git a/plugins/pychrysalide/glibext/strbuilder.c b/plugins/pychrysalide/glibext/strbuilder.c index 482f7df..a6de0f0 100644 --- a/plugins/pychrysalide/glibext/strbuilder.c +++ b/plugins/pychrysalide/glibext/strbuilder.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * strbuilder.c - équivalent Python du fichier "glibext/strbuilder.c" * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -37,11 +37,22 @@ +/* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ + + /* Procède à l'initialisation de l'interface d'exportation. */ static void py_string_builder_interface_init(GStringBuilderInterface *, gpointer *); /* Exporte une chaîne de caractères à partir d'un objet. */ -bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *); +static bool py_string_builder_to_string_wrapper(const GStringBuilder *, unsigned int, sized_binary_t *); + + + +/* ------------------------- CONNEXION AVEC L'API DE PYTHON ------------------------- */ + + +/* Transmet la description d'un objet définie par son parent. */ +static PyObject *py_string_builder_parent_to_string(PyObject *, PyObject *); /* Exporte une chaîne de caractères à partir d'un objet. */ static PyObject *py_string_builder_to_string(PyObject *, PyObject *); @@ -51,6 +62,11 @@ static PyObject *py_string_builder_str(PyObject *); +/* ---------------------------------------------------------------------------------- */ +/* GLUE POUR CREATION DEPUIS PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + /****************************************************************************** * * * Paramètres : iface = interface GLib à initialiser. * @@ -98,9 +114,9 @@ static void py_string_builder_interface_init(GStringBuilderInterface *iface, gpo * * ******************************************************************************/ -bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) +static bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned int flags, sized_binary_t *out) { - bool result; /* Bilan à retourner */ + bool result; /* Bilan à retourner */ PyGILState_STATE gstate; /* Sauvegarde d'environnement */ PyObject *pyobj; /* Objet Python concerné */ PyObject *args; /* Arguments pour l'appel */ @@ -119,7 +135,10 @@ bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned "The optional *flags* argument define hints for the operation" \ " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ "\n" \ - "The result has to be a string or *None* in case of error." \ + "The result has to be a string." \ + "\n" \ + "A *TypeError* exception is raised if the return value is not" \ + " a string." \ ) result = false; @@ -128,38 +147,37 @@ bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned pyobj = pygobject_new(G_OBJECT(builder)); - if (has_python_method(pyobj, "_to_string")) - { - args = PyTuple_New(1); - PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags)); + args = PyTuple_New(1); + PyTuple_SetItem(args, 0, PyLong_FromUnsignedLong(flags)); - pyret = run_python_method(pyobj, "_to_string", args); + pyret = run_python_method(pyobj, "_to_string", args); - if (pyret != NULL && pyret != Py_None) + if (pyret != NULL) + { + if (PyUnicode_Check(pyret)) { - if (PyUnicode_Check(pyret)) - { - utf8 = PyUnicode_AsUTF8AndSize(pyret, &size); - - if (utf8 != NULL) - { - assert(size >= 0); + utf8 = PyUnicode_AsUTF8AndSize(pyret, &size); - add_to_sized_binary(out, utf8, size); - result = true; + if (utf8 != NULL) + { + assert(size >= 0); - } + add_to_sized_binary(out, utf8, size); + result = true; } } - Py_XDECREF(pyret); - - Py_DECREF(args); + if (!result) + PyErr_SetString(PyExc_TypeError, _("object description has to get provided as an UTF-8 string value")); } + Py_XDECREF(pyret); + + Py_DECREF(args); + Py_DECREF(pyobj); PyGILState_Release(gstate); @@ -169,6 +187,108 @@ bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned } + +/* ---------------------------------------------------------------------------------- */ +/* CONNEXION AVEC L'API DE PYTHON */ +/* ---------------------------------------------------------------------------------- */ + + +/****************************************************************************** +* * +* Paramètres : self = objet dont l'instance se veut unique. * +* args = adresse non utilisée ici. * +* * +* Description : Transmet la description d'un objet définie par son parent. * +* * +* Retour : Présentation de l'élément construite. * +* * +* Remarques : - * +* * +******************************************************************************/ + +static PyObject *py_string_builder_parent_to_string(PyObject *self, PyObject *args) +{ + PyObject *result; /* Valeur à retourner */ + unsigned int flags; /* Eventuelles indications */ + int ret; /* Bilan de lecture des args. */ + GStringBuilder *builder; /* Mécanismes natifs */ + GStringBuilderInterface *iface; /* Interface utilisée */ + GStringBuilderInterface *parent_iface; /* Interface parente */ + sized_binary_t out; /* Description construite */ + bool status; /* Bilan de l'opération */ + +#define STRING_BUILDER_PARENT_TO_STRING_METHOD PYTHON_METHOD_DEF \ +( \ + parent_to_string, "$self, /, flags=0", \ + METH_VARARGS, py_string_builder, \ + "Provide a string representation defined by the interface" \ + " implementation from the object native parent.\n" \ + "\n" \ + "The result is a string.\n" \ + "\n" \ + "A *TypeError* exception is raised if the object parent does" \ + " not implement the pychrysalide.glibext.StringBuilder" \ + " interface." \ + "\n" \ + "A *RuntimeError* exception is raised if the direct parent type"\ + " of the object has not a native implementation. For Python" \ + " implementations, the super()._to_string() function has to be" \ + " used instead.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ +) + + if (!check_for_native_parent(self)) + return NULL; + + flags = 0; + + ret = PyArg_ParseTuple(args, "|I", &flags); + if (!ret) return NULL; + + builder = G_STRING_BUILDER(pygobject_get(self)); + + iface = G_STRING_BUILDER_GET_IFACE(builder); + + parent_iface = g_type_interface_peek_parent(iface); + + if (parent_iface == NULL) + { + PyErr_SetString(PyExc_TypeError, _("object parent does not implement the StringBuilder interface")); + + result = NULL; + + } + else + { + init_sized_binary(&out); + + status = parent_iface->to_string(builder, flags, &out); + + if (status) + result = PyUnicode_FromStringAndSize(out.data, out.size); + + else + { + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + + } + + exit_sized_binary(&out); + + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + + } + + return result; + +} + + /****************************************************************************** * * * Paramètres : self = objet manipulé ici. * @@ -176,7 +296,7 @@ bool py_string_builder_to_string_wrapper(const GStringBuilder *builder, unsigned * * * Description : Exporte une chaîne de caractères à partir d'un objet. * * * -* Retour : Présentation de l'élément construite ou None. * +* Retour : Présentation de l'élément construite. * * * * Remarques : - * * * @@ -198,11 +318,13 @@ static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args) "Provide a string representation for the object which is used" \ " as the default implementation of the __repr__() method.\n" \ "\n" \ - "\n" \ "The optional *flags* argument define hints for the operation" \ " (for instance the Intel or AT&T flavor for x86 assembly).\n" \ "\n" \ - "The result is a string or *None* in case of error." \ + "The result is a string.\n" \ + "\n" \ + "A *BufferError* exception is raised if the description has" \ + " not been able to get created." \ ) flags = 0; @@ -221,12 +343,17 @@ static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args) else { - result = Py_None; - Py_INCREF(result); + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + } exit_sized_binary(&out); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + return result; } @@ -238,13 +365,13 @@ static PyObject *py_string_builder_to_string(PyObject *self, PyObject *args) * * * Description : Fournit une représentation de l'objet exportable. * * * -* Retour : Présentation de l'élément construite ou None. * +* Retour : Présentation de l'élément construite. * * * * Remarques : - * * * ******************************************************************************/ -PyObject *py_string_builder_str(PyObject *self) +static PyObject *py_string_builder_str(PyObject *self) { PyObject *result; /* Emplacement à retourner */ GStringBuilder *builder; /* Mécanismes natifs */ @@ -262,12 +389,17 @@ PyObject *py_string_builder_str(PyObject *self) else { - result = Py_None; - Py_INCREF(result); + result = NULL; + + if (PyErr_Occurred() == NULL) + PyErr_SetString(PyExc_BufferError, _("unable to create a description")); + } exit_sized_binary(&out); + CLEAN_RESULT_IF_RAISED_EXCEPTION(result); + return result; } @@ -289,6 +421,7 @@ PyTypeObject *get_python_string_builder_type(void) { static PyMethodDef py_string_builder_methods[] = { STRING_BUILDER_TO_STRING_WRAPPER, + STRING_BUILDER_PARENT_TO_STRING_METHOD, STRING_BUILDER_TO_STRING_METHOD, { NULL } }; diff --git a/plugins/pychrysalide/glibext/strbuilder.h b/plugins/pychrysalide/glibext/strbuilder.h index d6aae20..1881cae 100644 --- a/plugins/pychrysalide/glibext/strbuilder.h +++ b/plugins/pychrysalide/glibext/strbuilder.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * strbuilder.h - prototypes pour l'équivalent Python du fichier "glibext/strbuilder.h" * - * Copyright (C) 2021 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * diff --git a/plugins/pychrysalide/glibext/work.c b/plugins/pychrysalide/glibext/work.c index 6a15984..e6791e3 100644 --- a/plugins/pychrysalide/glibext/work.c +++ b/plugins/pychrysalide/glibext/work.c @@ -41,9 +41,9 @@ /* Initialise la classe des travaux programmés. */ -static void py_generic_work_init_gclass(GGenericWorkClass *, gpointer); +static int py_generic_work_init_gclass(GGenericWorkClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK, py_generic_work_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(generic_work, G_TYPE_GENERIC_WORK); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_generic_work_init(PyObject *, PyObject *, PyObject *); @@ -68,20 +68,22 @@ static PyObject *py_generic_work_process(PyObject *, PyObject *); /****************************************************************************** * * -* Paramètres : class = classe à initialiser. * -* unused = données non utilisées ici. * +* Paramètres : gclass = classe GLib à initialiser. * +* pyclass = classe Python à initialiser. * * * * Description : Initialise la classe des travaux programmés. * * * -* Retour : - * +* Retour : 0 pour indiquer un succès de l'opération. * * * * Remarques : - * * * ******************************************************************************/ -static void py_generic_work_init_gclass(GGenericWorkClass *class, gpointer unused) +static int py_generic_work_init_gclass(GGenericWorkClass *gclass, PyTypeObject *pyclass) { - class->run = py_generic_work_run_wrapper; + PY_CLASS_SET_WRAPPER(gclass->run, py_generic_work_run_wrapper); + + return 0; } @@ -296,6 +298,8 @@ bool ensure_python_generic_work_is_registered(void) dict = PyModule_GetDict(module); + pyg_register_class_init(G_TYPE_GENERIC_WORK, (PyGClassInitFunc)py_generic_work_init_gclass); + if (!register_class_for_pygobject(dict, G_TYPE_GENERIC_WORK, type)) return false; diff --git a/plugins/pychrysalide/glibext/workqueue.c b/plugins/pychrysalide/glibext/workqueue.c index d8126be..ca6c73c 100644 --- a/plugins/pychrysalide/glibext/workqueue.c +++ b/plugins/pychrysalide/glibext/workqueue.c @@ -94,9 +94,9 @@ static int py_work_queue_init(PyObject *self, PyObject *args, PyObject *kwds) { int ret; /* Bilan de lecture des args. */ -#define WORK_QUEUE_DOC \ - "WorkQueue defines a basic work aimed to get processed in a" \ - " thread setup by a pychrysalide.glibext.WorkQueue instance.\n" \ +#define WORK_QUEUE_DOC \ + "WorkQueue creates threads in order to process" \ + " pychrysalide.glibext.Work instances.\n" \ "\n" \ "Instances can be created using the following constructor:\n" \ "\n" \ diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c index ea2f55d..0c84278 100644 --- a/plugins/pychrysalide/helpers.c +++ b/plugins/pychrysalide/helpers.c @@ -552,7 +552,6 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type) * * * Paramètres : type = type du nouvel objet à mettre en place. * * gbase = type de base natif. * -* cinit = procédure d'initialisation de la classe associée. * * args = éventuelle liste d'arguments. * * kwds = éventuel dictionnaire de valeurs mises à disposition.* * * @@ -564,7 +563,7 @@ bool register_python_module_object(PyObject *module, PyTypeObject *type) * * ******************************************************************************/ -PyObject *python_abstract_constructor(PyTypeObject *type, GType gbase, GClassInitFunc cinit, PyObject *args, PyObject *kwds) +PyObject *python_abstract_constructor(PyTypeObject *type, GType gbase, PyObject *args, PyObject *kwds) { PyObject *result; /* Objet à retourner */ PyTypeObject *base; /* Type parent version Python */ @@ -1123,6 +1122,60 @@ int forward_pygobjet_init(PyObject *self) /****************************************************************************** * * +* Paramètres : type = type Python à ausculter. * +* * +* Description : Détermine si un type Python est implémenté en C ou non. * +* * +* Retour : Bilan de l'analyse. * +* * +* Remarques : - * +* * +******************************************************************************/ + +bool pytype_has_native_implementation(const PyTypeObject *type) +{ + bool result; /* Bilan à retourner */ + GType gtype; /* Type associé à la classe */ + + static GQuark pygobject_custom_key = 0; /* Clef d'accès direct */ + + /** + * Dans les sources de PyGObject, la fonction pyg_type_register() de + * gi/gimodule.c, appelée depuis gi/types.py, contient la bribe de code + * suivante : + * + * // Mark this GType as a custom python type + * g_type_set_qdata(instance_type, pygobject_custom_key, + * GINT_TO_POINTER (1)); + * + * La fonction pyi_object_register_types() de gi/pygobject-object.c indique + * la clef associée au Quark : + * + * pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + * + * Enfin, une fonction inspirante est codée dans le fichier gi/pygi-type.c : + * + * gboolean pyg_gtype_is_custom(GType gtype) + * { + * return g_type_get_qdata (gtype, pygobject_custom_key) != NULL; + * } + * + */ + + if (pygobject_custom_key == 0) + pygobject_custom_key = g_quark_from_static_string("PyGObject::custom"); + + gtype = pyg_type_from_object((PyObject *)type); + + result = (g_type_get_qdata(gtype, pygobject_custom_key) == NULL); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : arg = argument quelconque à tenter de convertir. * * dst = destination des valeurs récupérées en cas de succès. * * * diff --git a/plugins/pychrysalide/helpers.h b/plugins/pychrysalide/helpers.h index 133726a..0aaf976 100644 --- a/plugins/pychrysalide/helpers.h +++ b/plugins/pychrysalide/helpers.h @@ -34,6 +34,9 @@ #endif +#include <i18n.h> + + /* ---------------------- ACCELERATEURS POUR PYTHON UNIQUEMENT ---------------------- */ @@ -159,7 +162,7 @@ bool register_python_module_object(PyObject *, PyTypeObject *); /* Accompagne la création d'une instance dérivée en Python. */ -PyObject *python_abstract_constructor(PyTypeObject *, GType, GClassInitFunc, PyObject *, PyObject *); +PyObject *python_abstract_constructor(PyTypeObject *, GType, PyObject *, PyObject *); #define CREATE_DYN_CONSTRUCTOR(pyname, gbase) \ @@ -172,12 +175,12 @@ static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObj } -#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase, cinit) \ +#define CREATE_DYN_ABSTRACT_CONSTRUCTOR(pyname, gbase) \ static PyObject *py_ ## pyname ## _new(PyTypeObject *, PyObject *, PyObject *); \ static PyObject *py_ ## pyname ## _new(PyTypeObject *type, PyObject *args, PyObject *kwds) \ { \ PyObject *result; /* Objet à retourner */ \ - result = python_abstract_constructor(type, gbase, (GClassInitFunc)cinit, args, kwds); \ + result = python_abstract_constructor(type, gbase, args, kwds); \ return result; \ } @@ -225,6 +228,41 @@ PyTypeObject *define_python_dynamic_type(const PyTypeObject *); /** + * Prise en compte d'éventuelles exceptions levées dans les implémentations. + * + * Par exemple : + * - du code Python exécute une fonction implémentée en C ; + * - cette dernière fait appel à un Wrapper C qui sollicite du code + * d'implémentation Python. + * + * Cette seconde étape peut lever une exception (impletation manquante ou + * implémentation levant une exception. + * + * Les codes C des étapes 1 et 2 ne dispose pas de mécanismes pour transmettre + * le détail des éventuelles exceptions, mais Python le mémorise. + */ + +#define UPDATE_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + result = val; \ + } \ + while (0) + +#define CLEAN_RESULT_IF_RAISED_EXCEPTION(val) \ + do \ + { \ + if (PyErr_Occurred() != NULL) \ + { \ + Py_XDECREF(result); \ + result = NULL; \ + } \ + } \ + while (0) + + +/** * pygobject_new() prend en compte les références flottantes au moment de la * construction d'un objet Python. * @@ -250,11 +288,23 @@ bool register_class_for_pygobject(PyObject *, GType, PyTypeObject *); bool register_interface_for_pygobject(PyObject *, GType, PyTypeObject *, const GInterfaceInfo *); /* Enregistre un type Python dérivant d'un type GLib dynamique. */ -bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); +bool register_class_for_dynamic_pygobject(GType, PyTypeObject *); // REMME /* Fait suivre à la partie GObject une initialisation nouvelle. */ int forward_pygobjet_init(PyObject *); +/* Détermine si un type Python est implémenté en C ou non. */ +bool pytype_has_native_implementation(const PyTypeObject *); + +#define check_for_native_parent(obj) \ + ({ \ + bool __result; \ + __result = pytype_has_native_implementation((obj)->ob_type->tp_base); \ + if (!__result) \ + PyErr_SetString(PyExc_RuntimeError, _("object parent is not a native type")); \ + __result; \ + }) + /* Tente de convertir en valeur GType. */ int convert_to_gtype(PyObject *, void *); diff --git a/plugins/pychrysalide/plugins/plugin.c b/plugins/pychrysalide/plugins/plugin.c index b9db3bc..78f57ba 100644 --- a/plugins/pychrysalide/plugins/plugin.c +++ b/plugins/pychrysalide/plugins/plugin.c @@ -49,7 +49,7 @@ /* Initialise la classe des greffons d'extension. */ static int py_plugin_module_init_gclass(GPluginModuleClass *, PyTypeObject *); -CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE, py_plugin_module_init_gclass); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(plugin_module, G_TYPE_PLUGIN_MODULE); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_plugin_module_init(PyObject *self, PyObject *args, PyObject *kwds); diff --git a/plugins/pychrysalide/plugins/python.c b/plugins/pychrysalide/plugins/python.c index a958a8d..d6b9281 100644 --- a/plugins/pychrysalide/plugins/python.c +++ b/plugins/pychrysalide/plugins/python.c @@ -71,7 +71,7 @@ static char *g_python_plugin_get_modname(const GPythonPlugin *); /* ------------------------ GLUE POUR CREATION DEPUIS PYTHON ------------------------ */ -CREATE_DYN_ABSTRACT_CONSTRUCTOR(python_plugin, G_TYPE_PYTHON_PLUGIN, NULL); +CREATE_DYN_ABSTRACT_CONSTRUCTOR(python_plugin, G_TYPE_PYTHON_PLUGIN); /* Initialise une instance sur la base du dérivé de GObject. */ static int py_python_plugin_init(PyObject *self, PyObject *args, PyObject *kwds); diff --git a/src/common/Makefile.am b/src/common/Makefile.am index 687cb63..d09b661 100644 --- a/src/common/Makefile.am +++ b/src/common/Makefile.am @@ -58,7 +58,6 @@ libcommon4_la_SOURCES = \ extstr.h extstr.c \ fnv1a.h fnv1a.c \ io.h io.c \ - json.h json.c \ leb128.h leb128.c \ macros.h \ packed.h packed.c \ @@ -75,6 +74,13 @@ libcommon4_la_SOURCES += \ endif +if BUILD_JSONGLIB_SUPPORT + +libcommon4_la_SOURCES += \ + json.h json.c + +endif + libcommon4_la_CFLAGS = $(TOOLKIT_CFLAGS) $(LIBSSL_CFLAGS) $(LIBJSONGLIB_CFLAGS) if BUILD_CURL_SUPPORT @@ -83,6 +89,12 @@ libcommon4_la_CFLAGS += $(LIBCURL_CFLAGS) endif +if BUILD_JSONGLIB_SUPPORT + +libcommon4_la_CFLAGS += $(LIBJSONGLIB_CFLAGS) + +endif + devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) diff --git a/src/core/core.h b/src/core/core.h index 7c50f6c..e5f0a60 100644 --- a/src/core/core.h +++ b/src/core/core.h @@ -32,9 +32,11 @@ /* Eléments à (dé)charger disponibles */ typedef enum _AvailableCoreComponent { - ACC_NONE = (0 << 0), /* Statut initial */ - ACC_GLOBAL_VARS = (1 << 0), /* Singletons globaux */ - ACC_SCAN_FEATURES = (1 << 0), /* Espace de noms pour scan */ + ACC_NONE = (0 << 0), /* Statut initial */ + ACC_GLOBAL_VARS = (1 << 0), /* Singletons globaux */ + ACC_SCAN_FEATURES = (1 << 1), /* Espace de noms pour scan */ + + ACC_ALL_COMPONENTS = (1 << 2) - 1 } AvailableCoreComponent; diff --git a/src/glibext/Makefile.am b/src/glibext/Makefile.am index 738f5b3..2e9e86d 100644 --- a/src/glibext/Makefile.am +++ b/src/glibext/Makefile.am @@ -4,8 +4,6 @@ BUILT_SOURCES = chrysamarshal.h chrysamarshal.c resources.h resources.c noinst_LTLIBRARIES = libglibext.la libglibextui.la # libglibext_la_SOURCES = \ -# comparison-int.h \ -# comparison.h comparison.c \ # configuration-int.h \ # configuration.h configuration.c \ # gbinarycursor.h gbinarycursor.c \ @@ -40,6 +38,10 @@ noinst_LTLIBRARIES = libglibext.la libglibextui.la libglibext_la_SOURCES = \ chrysamarshal.h chrysamarshal.c \ + comparable-int.h \ + comparable.h comparable.c \ + hashable-int.h \ + hashable.h hashable.c \ helpers.h \ objhole-int.h \ objhole.h objhole.c \ diff --git a/src/glibext/comparable-int.h b/src/glibext/comparable-int.h new file mode 100644 index 0000000..18972c2 --- /dev/null +++ b/src/glibext/comparable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable-int.h - définitions internes propres aux opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_COMPARABLE_INT_H +#define _GLIBEXT_COMPARABLE_INT_H + + +#include "comparable.h" + + + +/* Réalise une comparaison étendue entre objets. */ +typedef int (* compare_object_fc) (const GComparableObject *, const GComparableObject *); + + +/* Instance d'objet comparable (interface) */ +struct _GComparableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + compare_object_fc compare; /* Comparaison étendue */ + +}; + + + +#endif /* _GLIBEXT_COMPARABLE_INT_H */ diff --git a/src/glibext/comparable.c b/src/glibext/comparable.c new file mode 100644 index 0000000..95e7de7 --- /dev/null +++ b/src/glibext/comparable.c @@ -0,0 +1,110 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.c - opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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 "comparable.h" + + +#include "comparable-int.h" + + + +/* Procède à l'initialisation de l'interface de comparaison. */ +static void g_comparable_object_default_init(GComparableObjectInterface *); + + + +/* Détermine le type d'une interface pour un objet comparable. */ +G_DEFINE_INTERFACE(GComparableObject, g_comparable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de comparaison. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_comparable_object_default_init(GComparableObjectInterface *iface) +{ + iface->compare = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Réalise une comparaison étendue entre objets. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +int g_comparable_object_compare(const GComparableObject *object, const GComparableObject *other) +{ + int result; /* Bilan à retourner */ + GComparableObjectInterface *iface; /* Interface utilisée */ + + iface = G_COMPARABLE_OBJECT_GET_IFACE(object); + + result = iface->compare(object, other); + + return result; + +} + + +/****************************************************************************** +* * +* Paramètres : object = premier objet à consulter pour une comparaison. * +* other = second objet à consulter pour une comparaison. * +* * +* Description : Détermine si deux objets sont fonctionnellement identiques. * +* * +* Retour : Bilan de la comparaison. * +* * +* Remarques : - * +* * +******************************************************************************/ + +gboolean g_comparable_object_is_equal(const GComparableObject *object, const GComparableObject *other) +{ + gboolean result; /* Bilan à renvoyer */ + int ret; /* Bilan d'une comparaison */ + + ret = g_comparable_object_compare(object, other); + + result = (ret == 0); + + return result; + +} diff --git a/src/glibext/comparable.h b/src/glibext/comparable.h new file mode 100644 index 0000000..7a652ff --- /dev/null +++ b/src/glibext/comparable.h @@ -0,0 +1,46 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * comparable.h - prototypes pour les opérations de comparaison d'objets + * + * Copyright (C) 2022-2025 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/>. + */ + + +#ifndef _GLIBEXT_COMPARABLE_H +#define _GLIBEXT_COMPARABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_COMPARABLE_OBJECT (g_comparable_object_get_type()) + +DECLARE_INTERFACE(GComparableObject, g_comparable_object, G, COMPARABLE_OBJECT); + + + +/* Réalise une comparaison étendue entre objets. */ +int g_comparable_object_compare(const GComparableObject *, const GComparableObject *); + +/* Détermine si deux objets sont fonctionnellement identiques. */ +gboolean g_comparable_object_is_equal(const GComparableObject *, const GComparableObject *); + + + +#endif /* _GLIBEXT_COMPARABLE_H */ diff --git a/src/glibext/comparison-int.h b/src/glibext/comparison-int.h deleted file mode 100644 index 446f25d..0000000 --- a/src/glibext/comparison-int.h +++ /dev/null @@ -1,58 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison-int.h - définitions internes propres aux opérations de comparaison d'objets - * - * Copyright (C) 2022 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 <http://www.gnu.org/licenses/>. - */ - - -#ifndef _GLIBEXT_COMPARISON_INT_H -#define _GLIBEXT_COMPARISON_INT_H - - -#include "comparison.h" - - - -/* Réalise une comparaison entre objets selon un critère précis. */ -typedef bool (* compare_rich_fc) (const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - -/* Instance d'élément comparable (interface) */ -struct _GComparableItemIface -{ - GTypeInterface base_iface; /* A laisser en premier */ - - compare_rich_fc cmp_rich; /* Comparaison de façon précise*/ - -}; - - -/* Redéfinition */ -typedef GComparableItemIface GComparableItemInterface; - - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_signed(long long, long long, RichCmpOperation); - -/* Réalise une comparaison riche entre valeurs entière. */ -bool compare_rich_integer_values_unsigned(unsigned long long, unsigned long long, RichCmpOperation); - - - -#endif /* _GLIBEXT_COMPARISON_INT_H */ diff --git a/src/glibext/comparison.c b/src/glibext/comparison.c deleted file mode 100644 index 8ce6941..0000000 --- a/src/glibext/comparison.c +++ /dev/null @@ -1,199 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.c - opérations de comparaison d'objets - * - * Copyright (C) 2022 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 "comparison.h" - - -#include <assert.h> - - -#include "comparison-int.h" - - - -/* Procède à l'initialisation de l'interface de comparaison. */ -static void g_comparable_item_default_init(GComparableItemInterface *); - - - -/* Détermine le type d'une interface pour un objet comparable. */ -G_DEFINE_INTERFACE(GComparableItem, g_comparable_item, G_TYPE_OBJECT) - - -/****************************************************************************** -* * -* Paramètres : iface = interface GLib à initialiser. * -* * -* Description : Procède à l'initialisation de l'interface de comparaison. * -* * -* Retour : - * -* * -* Remarques : - * -* * -******************************************************************************/ - -static void g_comparable_item_default_init(GComparableItemInterface *iface) -{ - -} - - -/****************************************************************************** -* * -* Paramètres : item = premier objet à consulter pour une comparaison. * -* other = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* status = bilan des opérations de comparaison. [OUT] * -* * -* Description : Réalise une comparaison entre objets selon un critère précis.* -* * -* Retour : true si la comparaison a pu être effectuée, false sinon. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool g_comparable_item_compare_rich(const GComparableItem *item, const GComparableItem *other, RichCmpOperation op, bool *status) -{ - bool result; /* Etat à retourner */ - GComparableItemIface *iface; /* Interface utilisée */ - - iface = G_COMPARABLE_ITEM_GET_IFACE(item); - - result = iface->cmp_rich(item, other, op, status); - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_signed(long long a, long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} - - -/****************************************************************************** -* * -* Paramètres : a = premier élément à consulter pour une comparaison. * -* b = second objet à consulter pour une comparaison. * -* op = opération de comparaison à réaliser. * -* * -* Description : Réalise une comparaison riche entre valeurs entière. * -* * -* Retour : Bilan des opérations de comparaison. * -* * -* Remarques : - * -* * -******************************************************************************/ - -bool compare_rich_integer_values_unsigned(unsigned long long a, unsigned long long b, RichCmpOperation op) -{ - bool result; /* Bilan à retourner */ - - switch (op) - { - case RCO_LT: - result = (a < b); - break; - - case RCO_LE: - result = (a <= b); - break; - - case RCO_EQ: - result = (a == b); - break; - - case RCO_NE: - result = (a != b); - break; - - case RCO_GT: - result = (a > b); - break; - - case RCO_GE: - result = (a >= b); - break; - - default: - assert(false); - result = false; - break; - - } - - return result; - -} diff --git a/src/glibext/comparison.h b/src/glibext/comparison.h deleted file mode 100644 index 8d43210..0000000 --- a/src/glibext/comparison.h +++ /dev/null @@ -1,80 +0,0 @@ - -/* Chrysalide - Outil d'analyse de fichiers binaires - * comparison.h - prototypes pour les opérations de comparaison d'objets - * - * Copyright (C) 2022 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/>. - */ - - -#ifndef _GLIBEXT_COMPARISON_H -#define _GLIBEXT_COMPARISON_H - - -#include <glib-object.h> -#include <stdbool.h> - - - -#define G_TYPE_COMPARABLE_ITEM (g_comparable_item_get_type()) -#define G_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), G_TYPE_COMPARABLE_ITEM, GComparableItem)) -#define G_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_CAST((vtable), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) -#define G_IS_COMPARABLE_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), G_TYPE_COMPARABLE_ITEM)) -#define G_IS_COMPARABLE_ITEM_CLASS(vtable) (G_TYPE_CHECK_CLASS_TYPE((vtable), G_TYPE_COMPARABLE_ITEM)) -#define G_COMPARABLE_ITEM_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE((inst), G_TYPE_COMPARABLE_ITEM, GComparableItemIface)) - - -/* Instance d'élément comparable (coquille vide) */ -typedef struct _GComparableItem GComparableItem; - -/* Instance d'élément comparable (interface) */ -typedef struct _GComparableItemIface GComparableItemIface; - - -/* Modes de comparaison */ -typedef enum _RichCmpOperation -{ - RCO_LT, /* Equivalent de '<' */ - RCO_LE, /* Equivalent de '<=' */ - RCO_EQ, /* Equivalent de '==' */ - RCO_NE, /* Equivalent de '!=' */ - RCO_GT, /* Equivalent de '>' */ - RCO_GE, /* Equivalent de '>°' */ - -} RichCmpOperation; - -/* Détermination d'un besoin de comparaison supplémentaire */ -#define STATUS_NOT_EQUAL(_s, _o) \ - ({ \ - bool __result; \ - if (_o == RCO_LE || _o == RCO_EQ || _o == RCO_GE) \ - __result = !_s; \ - else \ - __result = _s; \ - __result; \ - }) - - -/* Détermine le type d'une interface pour un objet comparable. */ -GType g_comparable_item_get_type(void) G_GNUC_CONST; - -/* Réalise une comparaison entre objets selon un critère précis. */ -bool g_comparable_item_compare_rich(const GComparableItem *, const GComparableItem *, RichCmpOperation, bool *); - - - -#endif /* _GLIBEXT_COMPARISON_H */ diff --git a/src/glibext/hashable-int.h b/src/glibext/hashable-int.h new file mode 100644 index 0000000..f8a85e1 --- /dev/null +++ b/src/glibext/hashable-int.h @@ -0,0 +1,47 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable-int.h - définitions internes propres aux calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 <http://www.gnu.org/licenses/>. + */ + + +#ifndef _GLIBEXT_HASHABLE_INT_H +#define _GLIBEXT_HASHABLE_INT_H + + +#include "hashable.h" + + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +typedef guint (* hash_object_fc) (const GHashableObject *); + + +/* Instance d'objet visant à être unique (interface) */ +struct _GHashableObjectInterface +{ + GTypeInterface base_iface; /* A laisser en premier */ + + hash_object_fc hash; /* Réduction en valeur */ + +}; + + + +#endif /* _GLIBEXT_HASHABLE_INT_H */ diff --git a/src/glibext/hashable.c b/src/glibext/hashable.c new file mode 100644 index 0000000..f988a90 --- /dev/null +++ b/src/glibext/hashable.c @@ -0,0 +1,82 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.c - calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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 "hashable.h" + + +#include "hashable-int.h" + + + +/* Procède à l'initialisation de l'interface de détermination. */ +static void g_hashable_object_default_init(GHashableObjectInterface *); + + + +/* Détermine le type d'une interface pour la réduction d'un objet à une valeur. */ +G_DEFINE_INTERFACE(GHashableObject, g_hashable_object, G_TYPE_OBJECT) + + +/****************************************************************************** +* * +* Paramètres : iface = interface GLib à initialiser. * +* * +* Description : Procède à l'initialisation de l'interface de détermination. * +* * +* Retour : - * +* * +* Remarques : - * +* * +******************************************************************************/ + +static void g_hashable_object_default_init(GHashableObjectInterface *iface) +{ + iface->hash = NULL; + +} + + +/****************************************************************************** +* * +* Paramètres : object = objet dont l'instance est à consulter. * +* * +* Description : Calcule l'empreinte sur 32 bits d'un objet. * +* * +* Retour : Valeur de représentation, unique pour l'objet ou non. * +* * +* Remarques : - * +* * +******************************************************************************/ + +guint g_hashable_object_hash(const GHashableObject *object) +{ + guint result; /* Valeur à retourner */ + GHashableObjectInterface *iface; /* Interface utilisée */ + + iface = G_HASHABLE_OBJECT_GET_IFACE(object); + + result = iface->hash(object); + + return result; + +} diff --git a/src/glibext/hashable.h b/src/glibext/hashable.h new file mode 100644 index 0000000..165c744 --- /dev/null +++ b/src/glibext/hashable.h @@ -0,0 +1,42 @@ + +/* Chrysalide - Outil d'analyse de fichiers binaires + * hashable.h - prototypes pour les calculs de l'empreinte d'un objet + * + * Copyright (C) 2025 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/>. + */ + + +#ifndef _GLIBEXT_HASHABLE_H +#define _GLIBEXT_HASHABLE_H + + +#include "helpers.h" + + + +#define G_TYPE_HASHABLE_OBJECT (g_hashable_object_get_type()) + +DECLARE_INTERFACE(GHashableObject, g_hashable_object, G, HASHABLE_OBJECT); + + +/* Calcule l'empreinte sur 32 bits d'un objet. */ +guint g_hashable_object_hash(const GHashableObject *); + + + +#endif /* _GLIBEXT_HASHABLE_H */ diff --git a/src/glibext/singleton-int.h b/src/glibext/singleton-int.h index 9212e2e..747e64a 100644 --- a/src/glibext/singleton-int.h +++ b/src/glibext/singleton-int.h @@ -38,11 +38,14 @@ typedef GSingletonCandidate ** (* list_inner_instances_fc) (const GSingletonCand /* Met à jour une liste de candidats embarqués par un candidat. */ typedef void (* update_inner_instances_fc) (GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Fournit l'empreinte d'un candidat à une centralisation. */ -typedef guint (* hash_candidate_fc) (const GSingletonCandidate *); +/* Marque un candidat comme figé. */ +typedef void (* mark_candidate_as_ro_fc) (GSingletonCandidate *); -/* Détermine si deux candidats à l'unicité sont identiques. */ -typedef gboolean (* is_candidate_equal_fc) (const GSingletonCandidate *, const GSingletonCandidate *); +/* Indique si le candidat est figé. */ +typedef bool (* is_candidate_ro_fc) (const GSingletonCandidate *); + +/* Crée une copie modifiable d'un object unique. */ +typedef GSingletonCandidate * (* dup_candidate_fc) (const GSingletonCandidate *); /* Instance d'objet visant à être unique (interface) */ @@ -53,8 +56,10 @@ struct _GSingletonCandidateInterface list_inner_instances_fc list_inner; /* Récupération d'internes */ update_inner_instances_fc update_inner; /* Mise à jour des éléments */ - hash_candidate_fc hash; /* Prise d'empreinte */ - is_candidate_equal_fc is_equal; /* Comparaison */ + mark_candidate_as_ro_fc mark_as_ro; /* Bascule en mode figé */ + is_candidate_ro_fc is_ro; /* Consultation de l'état */ + + dup_candidate_fc dup; /* Création de copie modifiable*/ }; diff --git a/src/glibext/singleton.c b/src/glibext/singleton.c index 65b83e7..0a50d19 100644 --- a/src/glibext/singleton.c +++ b/src/glibext/singleton.c @@ -26,9 +26,10 @@ #include <assert.h> #include <malloc.h> -#include <stdbool.h> +#include "comparable.h" +#include "hashable.h" #include "singleton-int.h" @@ -39,11 +40,14 @@ /* Procède à l'initialisation de l'interface de rassemblement. */ static void g_singleton_candidate_default_init(GSingletonCandidateInterface *); +/* Fournit une liste de candidats embarqués par un candidat. */ +static GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); + /* Met à jour une liste de candidats embarqués par un candidat. */ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *, GSingletonCandidate **, size_t); -/* Détermine si deux candidats à l'unicité sont identiques. */ -static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *, GList **); +/* Marque un candidat comme figé. */ +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *); @@ -70,7 +74,9 @@ static void g_singleton_factory_finalize(GSingletonFactory *); /* Détermine le type d'une interface pour la constitution d'objets uniques. */ -G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) +G_DEFINE_INTERFACE_WITH_CODE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT, + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_HASHABLE_OBJECT); + g_type_interface_add_prerequisite(g_define_type_id, G_TYPE_COMPARABLE_OBJECT)) /****************************************************************************** @@ -87,6 +93,13 @@ G_DEFINE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G_TYPE_OBJECT) static void g_singleton_candidate_default_init(GSingletonCandidateInterface *iface) { + iface->list_inner = NULL; + iface->update_inner = NULL; + + iface->mark_as_ro = NULL; + iface->is_ro = NULL; + + iface->dup = NULL; } @@ -113,8 +126,11 @@ GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingleto if (iface->list_inner == NULL) { + assert(iface->update_inner == NULL); + *count = 0; result = NULL; + } else @@ -161,17 +177,16 @@ static void g_singleton_candidate_update_inner_instances(GSingletonCandidate *ca * * * Paramètres : candidate = objet dont l'instance se veut unique. * * * -* Description : Fournit l'empreinte d'un candidat à une centralisation. * +* Description : Marque un candidat comme figé. * * * -* Retour : Empreinte de l'élément représenté. * +* Retour : - * * * * Remarques : - * * * ******************************************************************************/ -guint g_singleton_candidate_hash(GSingletonCandidate *candidate) +static void g_singleton_candidate_mark_as_read_only(GSingletonCandidate *candidate) { - guint result; /* Valeur à retourner */ GSingletonCandidateInterface *iface; /* Interface utilisée */ GSingletonCandidate **children; /* Instances internes */ size_t count; /* Quantité de ces instances */ @@ -179,103 +194,62 @@ guint g_singleton_candidate_hash(GSingletonCandidate *candidate) iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - result = iface->hash(candidate); + iface->mark_as_ro(candidate); children = g_singleton_candidate_list_inner_instances(candidate, &count); for (i = 0; i < count; i++) { - result ^= g_singleton_candidate_hash(children[i]); - unref_object(children[i]); + g_singleton_candidate_mark_as_read_only(children[i]); + unref_object(G_OBJECT(children[i])); } if (children != NULL) free(children); - return result; - } /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * -* processed = liste de candidats déjà traités. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Indique si le candidat est figé. * * * -* Retour : Bilan de la comparaison. * +* Retour : true si le contenu du candidat ne peut plus être modifié. * * * * Remarques : - * * * ******************************************************************************/ -static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other, GList **processed) +bool g_singleton_candidate_is_read_only(const GSingletonCandidate *candidate) { - gboolean result; /* Bilan à renvoyer */ - GList *skip; /* Détection de boucle */ + bool result; /* Etat à retourner */ GSingletonCandidateInterface *iface; /* Interface utilisée */ - GSingletonCandidate **children[2]; /* Instances internes */ - size_t count[2]; /* Quantité de ces instances */ +#ifndef NDEBUG + GSingletonCandidate **children; /* Instances internes */ + size_t count; /* Quantité de ces instances */ size_t i; /* Boucle de parcours */ +#endif - skip = g_list_find(processed[0], candidate); - - if (skip != NULL) - result = (g_list_find(processed[1], other) != NULL); - - else - { - iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - - result = iface->is_equal(candidate, other); - - processed[0] = g_list_append(processed[0], candidate); - processed[1] = g_list_append(processed[1], other); - - if (!result) - goto done; - - children[0] = g_singleton_candidate_list_inner_instances(candidate, &count[0]); - children[1] = g_singleton_candidate_list_inner_instances(other, &count[1]); - - if (count[0] != count[1]) - { - for (i = 0; i < count[0]; i++) - g_object_unref(G_OBJECT(children[0][i])); - - for (i = 0; i < count[1]; i++) - g_object_unref(G_OBJECT(children[1][i])); - - } - - else - { - for (i = 0; i < count[0] && result; i++) - { - result = _g_singleton_candidate_is_equal(children[0][i], children[1][i], processed); - g_object_unref(G_OBJECT(children[0][i])); - g_object_unref(G_OBJECT(children[1][i])); - } - - for (; i < count[0]; i++) - { - g_object_unref(G_OBJECT(children[0][i])); - g_object_unref(G_OBJECT(children[1][i])); - } + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); - if (children[0] != NULL) - free(children[0]); + result = iface->is_ro(candidate); - if (children[1] != NULL) - free(children[1]); +#ifndef NDEBUG - } + children = g_singleton_candidate_list_inner_instances(candidate, &count); + for (i = 0; i < count; i++) + { + assert(result == g_singleton_candidate_is_read_only(children[i])); + unref_object(G_OBJECT(children[i])); } - done: + if (children != NULL) + free(children); + +#endif return result; @@ -285,31 +259,60 @@ static gboolean _g_singleton_candidate_is_equal(GSingletonCandidate *candidate, /****************************************************************************** * * * Paramètres : candidate = objet dont l'instance se veut unique. * -* other = second élément à analyser. * * * -* Description : Détermine si deux candidats à l'unicité sont identiques. * +* Description : Crée une copie modifiable d'un object unique. * * * -* Retour : Bilan de la comparaison. * +* Retour : Nouvelle instance mise en place. * * * * Remarques : - * * * ******************************************************************************/ -gboolean g_singleton_candidate_is_equal(GSingletonCandidate *candidate, GSingletonCandidate *other) +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *candidate) { - gboolean result; /* Bilan à renvoyer */ - GList *processed[2]; /* Suivi des traitements */ + GSingletonCandidate *result; /* Instance à retourner */ + GSingletonCandidateInterface *iface; /* Interface utilisée */ + size_t count; /* Quantité d'objets internes */ + GSingletonCandidate **children; /* Liste d'instances internes */ + size_t i; /* Boucle de parcours */ + GSingletonCandidate **new_children; /* Nouvelle liste d'instances */ + + iface = G_SINGLETON_CANDIDATE_GET_IFACE(candidate); + + result = iface->dup(candidate); + + assert(!g_singleton_candidate_is_read_only(result)); + + children = g_singleton_candidate_list_inner_instances(candidate, &count); + + if (count > 0) + { + new_children = malloc(count * sizeof(GSingletonCandidate *)); + + for (i = 0; i < count; i++) + { + new_children[i] = g_singleton_candidate_dup(children[i]); + + assert(!g_singleton_candidate_is_read_only(new_children[i])); + + } + + g_singleton_candidate_update_inner_instances(result, new_children, count); - processed[0] = NULL; - processed[1] = NULL; + for (i = 0; i < count; i++) + { + unref_object(G_OBJECT(new_children[i])); + unref_object(G_OBJECT(children[i])); + } + + free(new_children); - result = _g_singleton_candidate_is_equal(candidate, other, processed); + } - assert(processed[0] != NULL); - assert(processed[1] != NULL); + if (children != NULL) + free(children); - g_list_free(processed[0]); - g_list_free(processed[1]); + assert(G_OBJECT_TYPE(result) == G_OBJECT_TYPE(candidate)); return result; @@ -364,8 +367,8 @@ static void g_singleton_factory_class_init(GSingletonFactoryClass *klass) static void g_singleton_factory_init(GSingletonFactory *factory) { - factory->table = g_hash_table_new_full((GHashFunc)g_singleton_candidate_hash, - (GEqualFunc)g_singleton_candidate_is_equal, + factory->table = g_hash_table_new_full((GHashFunc)g_hashable_object_hash, + (GEqualFunc)g_comparable_object_is_equal, g_object_unref, NULL); g_mutex_init(&factory->access); @@ -524,6 +527,8 @@ GSingletonCandidate *g_singleton_factory_get_instance(GSingletonFactory *factory g_hash_table_add(factory->table, candidate); #endif + g_singleton_candidate_mark_as_read_only(candidate); + result = candidate; } diff --git a/src/glibext/singleton.h b/src/glibext/singleton.h index 36714fa..11afffd 100644 --- a/src/glibext/singleton.h +++ b/src/glibext/singleton.h @@ -25,6 +25,9 @@ #define _GLIBEXT_SINGLETON_H +#include <stdbool.h> + + #include "helpers.h" @@ -37,14 +40,11 @@ DECLARE_INTERFACE(GSingletonCandidate, g_singleton_candidate, G, SINGLETON_CANDIDATE); -/* Fournit une liste de candidats embarqués par un candidat. */ -GSingletonCandidate **g_singleton_candidate_list_inner_instances(const GSingletonCandidate *, size_t *); - -/* Fournit l'empreinte d'un candidat à une centralisation. */ -guint g_singleton_candidate_hash(GSingletonCandidate *); +/* Indique si le candidat est figé. */ +bool g_singleton_candidate_is_read_only(const GSingletonCandidate *); -/* Détermine si deux candidats à l'unicité sont identiques. */ -gboolean g_singleton_candidate_is_equal(GSingletonCandidate *, GSingletonCandidate *); +/* Crée une copie modifiable d'un object unique. */ +GSingletonCandidate *g_singleton_candidate_dup(const GSingletonCandidate *); diff --git a/src/glibext/strbuilder-int.h b/src/glibext/strbuilder-int.h index 97e0f4e..d74e65c 100644 --- a/src/glibext/strbuilder-int.h +++ b/src/glibext/strbuilder-int.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * strbuilder-int.h - définitions internes propres aux éléments exportant une chaîne de caractères * - * Copyright (C) 2024 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * diff --git a/src/glibext/strbuilder.c b/src/glibext/strbuilder.c index 54a102b..e48674c 100644 --- a/src/glibext/strbuilder.c +++ b/src/glibext/strbuilder.c @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * strbuilder.c - exportation d'une chaîne de caractères depuis un élément * - * Copyright (C) 2024 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * @@ -51,6 +51,7 @@ G_DEFINE_INTERFACE(GStringBuilder, g_string_builder, G_TYPE_OBJECT) static void g_string_builder_default_init(GStringBuilderInterface *iface) { + iface->to_string = NULL; } @@ -77,7 +78,11 @@ bool g_string_builder_to_string(const GStringBuilder *builder, unsigned int flag iface = G_STRING_BUILDER_GET_IFACE(builder); - result = iface->to_string(builder, flags, out); + if (iface->to_string == NULL) + result = false; + + else + result = iface->to_string(builder, flags, out); return result; diff --git a/src/glibext/strbuilder.h b/src/glibext/strbuilder.h index d8d0eaa..b6422f7 100644 --- a/src/glibext/strbuilder.h +++ b/src/glibext/strbuilder.h @@ -2,7 +2,7 @@ /* Chrysalide - Outil d'analyse de fichiers binaires * strbuilder.h - prototypes pour l'exportation d'une chaîne de caractères depuis un élément * - * Copyright (C) 2024 Cyrille Bagard + * Copyright (C) 2025 Cyrille Bagard * * This file is part of Chrysalide. * diff --git a/src/gtkext/Makefile.am b/src/gtkext/Makefile.am index 2a2738a..fd6c575 100644 --- a/src/gtkext/Makefile.am +++ b/src/gtkext/Makefile.am @@ -55,7 +55,7 @@ libgtkext4_la_CFLAGS = $(LIBGTK4_CFLAGS) devdir = $(includedir)/chrysalide/$(subdir:src/%=core/%) -dev_HEADERS = $(libgtkext_la_SOURCES:%c=) +dev_HEADERS = $(libgtkext4_la_SOURCES:%c=) #SUBDIRS = graph diff --git a/src/plugins/native-int.h b/src/plugins/native-int.h index 8b8e0eb..575994f 100644 --- a/src/plugins/native-int.h +++ b/src/plugins/native-int.h @@ -41,7 +41,18 @@ struct _GNativePlugin { GPluginModule parent; /* A laisser en premier */ - GModule *module; /* Abstration de manipulation */ + /** + * Le module porte le code et les données en mémoire. + * + * Les fonctions *_dispose() et *_finalize() accompagnant la libération des + * greffons de la mémoire ne peuvent donc pas libérer ce module car elles + * scieraient la branche sur laquelle elles se trouvent. + * + * Par ailleurs, même s'ils sont conservés dans chaque greffon, les modules + * sont mis en place dans le code principal. C'est donc ce dernier qui les + * libère, dans la fonction on_plugin_ref_toggle(). + */ + GModule *module; /* Structure de chargement GLib*/ }; diff --git a/src/plugins/native.c b/src/plugins/native.c index fedccbe..de20abe 100644 --- a/src/plugins/native.c +++ b/src/plugins/native.c @@ -131,12 +131,6 @@ static void g_native_plugin_init(GNativePlugin *plugin) static void g_native_plugin_dispose(GNativePlugin *plugin) { - if (plugin->module != NULL) - { - g_module_close(plugin->module); - plugin->module = NULL; - } - G_OBJECT_CLASS(g_native_plugin_parent_class)->dispose(G_OBJECT(plugin)); } @@ -194,6 +188,29 @@ bool g_native_plugin_create(GNativePlugin *plugin, const char *name, const char } +/****************************************************************************** +* * +* Paramètres : plugin = greffon à consulter. * +* * +* Description : Renvoie la structure opaque associée au module en mémoire. * +* * +* Retour : Structure de chargement côté GLib. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GModule *g_native_plugin_get_module(const GNativePlugin *plugin) +{ + GModule *result; /* Accès au module à renvoyer */ + + result = plugin->module; + + return result; + +} + + /* ---------------------------------------------------------------------------------- */ /* IMPLEMENTATION DES FONCTIONS DE CLASSE */ diff --git a/src/plugins/native.h b/src/plugins/native.h index 205342c..18039c8 100644 --- a/src/plugins/native.h +++ b/src/plugins/native.h @@ -26,6 +26,9 @@ #define _PLUGINS_NATIVE_H +#include <gmodule.h> + + #include "../glibext/helpers.h" @@ -35,5 +38,9 @@ DECLARE_GTYPE(GNativePlugin, g_native_plugin, G, NATIVE_PLUGIN); +/* Renvoie la structure opaque associée au module en mémoire. */ +GModule *g_native_plugin_get_module(const GNativePlugin *); + + #endif /* _PLUGINS_NATIVE_H */ diff --git a/src/plugins/pglist.c b/src/plugins/pglist.c index 694d138..6dc2d9c 100644 --- a/src/plugins/pglist.c +++ b/src/plugins/pglist.c @@ -37,9 +37,10 @@ #include "manager.h" +#include "native.h" #include "plugin-int.h" #include "../common/cpp.h" -#include "../common/extstr.h" // REMME ? +#include "../common/extstr.h" #include "../core/logs.h" #include "../core/nox.h" #include "../core/paths.h" @@ -69,6 +70,9 @@ static void browse_directory_for_plugins(const char *); /* Suit les variations du compteur de références d'un greffon. */ static void on_plugin_ref_toggle(gpointer, GPluginModule *, gboolean); +/* Fournit le greffon répondant à un nom donné. */ +static GPluginModule *_find_plugin_by_name(const char *, size_t *); + /****************************************************************************** @@ -115,8 +119,6 @@ bool init_all_plugins(bool load) if (load) load_remaning_plugins(); - exit: - return result; } @@ -136,71 +138,23 @@ bool init_all_plugins(bool load) void exit_all_plugins(void) { - -#if 0 ////// - - size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Définition du greffon */ lock_plugin_list_for_reading(); - if (_pg_list != NULL) + for (i = 0; i < _pg_count; i++) { - for (i = 0; i < _pg_count; i++) - { - assert(_pg_list[i] != NULL); - - /** - * Si le greffon a conduit à la mise en place d'autres greffons, le - * système de dépendances ne suffit pas pour le décompte des références : - * le greffon voit à un instant T son compteur décroître ici ; à un - * instant T+1, un greffon fils décrémente à son tour le compteur vers - * le greffon principal. - * - * Le compteur du conteneur tombe alors à 0, et le code correspondant - * est retiré. Lorsque que le flot d'exécution revient à la procédure - * de sortie du second greffon, son code n'est plus en mémoire. - * - * On s'assure donc que les greffons qui génèrent d'autres greffons - * sont bien traités en dernier. - */ - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface != NULL && pg_iface->container) - g_object_ref(_pg_list[i]); - - g_object_unref(_pg_list[i]); - - } - - for (i = 0; i < _pg_count; i++) - { - if (_pg_list[i] == NULL) - continue; - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface == NULL || !pg_iface->container) - continue; - - g_object_unref(_pg_list[i]); - - } + assert(_pg_list[i] != NULL); + unref_object(_pg_list[i]); + } + if (_pg_list != NULL) free(_pg_list); - } - unlock_plugin_list_for_reading(); g_rw_lock_clear(&_pg_lock); - - -#endif - } @@ -487,6 +441,7 @@ static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolea const char *name; /* Désignation du greffon */ size_t index; /* Indice du greffon */ GPluginModule *same; /* Juste pour la récupération */ + GModule *module; /* Structure de chargement GLib*/ if (last) { @@ -494,15 +449,44 @@ static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolea name = g_plugin_module_get_name(plugin); - same = get_plugin_by_name(name, &index); + /** + * Les mécanismes de g_object_unref() prennent en compte la bascule d'un + * compteur de références initialement à 2 avant appel pour déclencher + * cet appel à on_plugin_ref_toggle() mis en place par g_object_add_toggle_ref(). + * + * Incrémenter ce compteur à nouveau, via get_plugin_by_name(), puis le + * décrémenter ensuite via unref_object() va conduire à une nouvelle + * bascule des statuts de suivi dans g_object_unref(). + * + * Il est ainsi impératif de rechercher une instance du greffon dans + * la liste des extensions sans toucher au compteur de références. + */ + + same = _find_plugin_by_name(name, &index); + assert(same != NULL); assert(same == plugin); - g_clear_object(&_pg_list[index]); + _pg_list[index] = NULL; + + /** + * Suppression de la dernière référence. + */ + + if (G_IS_NATIVE_PLUGIN(plugin)) + module = g_native_plugin_get_module(G_NATIVE_PLUGIN(plugin)); + else + module = NULL; g_object_remove_toggle_ref(G_OBJECT(same), (GToggleNotify)on_plugin_ref_toggle, NULL); - unref_object(same); + /** + * Plus aucun code issu du greffon n'est désormais utile. Le module associé peut + * être libéré de la mémoire. + */ + + if (module != NULL) + g_module_close(module); } @@ -624,13 +608,18 @@ void load_remaning_plugins(void) /* Supprime les greffons non chargés */ - for (i = 0; i < _pg_count; i++) + for (i = 0; i < _pg_count;) { flags = g_plugin_module_get_flags(_pg_list[i]); - if ((flags & PSF_LOADED) == 0) + if (flags & PSF_LOADED) + i++; + + else { - g_object_unref(G_OBJECT(_pg_list[i])); + unref_object(_pg_list[i]); + + assert(_pg_list[i] == NULL); memmove(&_pg_list[i], &_pg_list[i + 1], (_pg_count - i - 1) * sizeof(GPluginModule *)); _pg_count--; @@ -657,11 +646,12 @@ void load_remaning_plugins(void) * * * Retour : Instance du greffon trouvé ou NULL si aucun. * * * -* Remarques : - * +* Remarques : Le compteur de référence d'un greffon trouvé n'est pas * +* modifié. * * * ******************************************************************************/ -GPluginModule *get_plugin_by_name(const char *name, size_t *index) +static GPluginModule *_find_plugin_by_name(const char *name, size_t *index) { GPluginModule *result; /* Greffon trouvé à renvoyer */ size_t i; /* Boucle de parcours */ @@ -684,7 +674,6 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) if (strcmp(current, name) == 0) { result = _pg_list[i]; - ref_object(result); if (index != NULL) *index = i; @@ -700,6 +689,33 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) /****************************************************************************** * * +* Paramètres : name = désignation du greffon recherché. * +* index = indice du greffon trouvé. [OUT] * +* * +* Description : Fournit le greffon répondant à un nom donné. * +* * +* Retour : Instance du greffon trouvé ou NULL si aucun. * +* * +* Remarques : - * +* * +******************************************************************************/ + +GPluginModule *get_plugin_by_name(const char *name, size_t *index) +{ + GPluginModule *result; /* Greffon trouvé à renvoyer */ + + result = _find_plugin_by_name(name, index); + + if (result != NULL) + ref_object(result); + + return result; + +} + + +/****************************************************************************** +* * * Paramètres : count = nombre de greffons trouvés. [OUT] * * * * Description : Fournit la liste de l'ensemble des greffons. * diff --git a/src/plugins/plugin.c b/src/plugins/plugin.c index d14e656..b7f85d5 100644 --- a/src/plugins/plugin.c +++ b/src/plugins/plugin.c @@ -135,34 +135,45 @@ static void g_plugin_module_init(GPluginModule *plugin) static void g_plugin_module_dispose(GPluginModule *plugin) { size_t i; /* Boucle de parcours */ + size_t index; /* Indice de greffon visé */ GPluginModule *dependency; /* Module nécessaire */ GPluginModuleClass *class; /* Classe de l'instance active */ - lock_plugin_list_for_reading(); - - for (i = 0; i < plugin->required_count; i++) + if (plugin->dependencies != NULL) { - dependency = get_plugin_by_name(plugin->required[i], NULL); + lock_plugin_list_for_reading(); - /* Si le chargement a bien été complet avant la sortie... */ - if (dependency != NULL) + for (i = 0; i < plugin->required_count; i++) { - /* Un coup pour l'appel à get_plugin_by_name(). */ - unref_object(dependency); + dependency = get_plugin_by_name(plugin->required[i], &index); + + /* Si la dépendance a bien été pris en compte... */ + if (test_in_bit_field(plugin->dependencies, index)) + { + assert(dependency != NULL); - /* Un coup pour la dépendance */ - unref_object(dependency); + /* Un coup pour l'appel à get_plugin_by_name(). */ + unref_object(dependency); + + /* Un coup pour la dépendance */ + unref_object(dependency); + + } } + unlock_plugin_list_for_reading(); + } - unlock_plugin_list_for_reading(); + if (plugin->flags & PSF_LOADED) + { + class = G_PLUGIN_MODULE_GET_CLASS(plugin); - class = G_PLUGIN_MODULE_GET_CLASS(plugin); + if (class->disable != NULL) + class->disable(plugin); - if (class->disable != NULL) - class->disable(plugin); + } G_OBJECT_CLASS(g_plugin_module_parent_class)->dispose(G_OBJECT(plugin)); @@ -185,6 +196,8 @@ static void g_plugin_module_finalize(GPluginModule *plugin) { size_t i; /* Boucle de parcours */ + printf("[!!!] Finalizing plugin %s\n", plugin->name); + if (plugin->name != NULL) free(plugin->name); diff --git a/tests/glibext/comparable.py b/tests/glibext/comparable.py new file mode 100644 index 0000000..48291ca --- /dev/null +++ b/tests/glibext/comparable.py @@ -0,0 +1,132 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import ComparableObject + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.ComparableObject.""" + + + def testComparableObjectCreation(self): + """Create objects with ComparableObject interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'ComparableObject can not be constructed'): + + co = ComparableObject() + + + class NewComparableObjectImplem(gi._gi.GObject, ComparableObject): + pass + + nco = NewComparableObjectImplem() + + self.assertIsNotNone(nco) + + + class NewComparableObjectImplem2(GObject.Object, ComparableObject): + pass + + nco2 = NewComparableObjectImplem() + + self.assertIsNotNone(nco2) + + + def testComparableObjectMethods(self): + """Test the ComparableObject methods.""" + + class BasicComparableObjectImplem(GObject.Object, ComparableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _compare(self, other): + if self._val < other._val: + status = -1 + elif self._val > other._val: + status = 1 + else: + status = 0 + return status + + + a = BasicComparableObjectImplem(123) + b = BasicComparableObjectImplem(456) + + self.assertTrue(a <= b) + + # Sans l'action de inherit_interface_slots(), c'est pygobject_richcompare() qui est appelée, + # laquelle compare simplement les adresses des pointeurs + + c = BasicComparableObjectImplem(789) + d = BasicComparableObjectImplem(234) + + self.assertTrue(c > d) + + + def testComparableObjectExceptions(self): + """Raise exceptions from the ComparableObject interface as expected.""" + + + class OtherComparableObject(GObject.Object, ComparableObject): + pass + + other = OtherComparableObject() + + + class BadComparableObjectImplem(GObject.Object, ComparableObject): + pass + + obj = BadComparableObjectImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_compare'"): + + s = obj < other + + + class BadComparableObjectImplem2(GObject.Object, ComparableObject): + + def _compare(self, other): + return 'AAA' + + obj2 = BadComparableObjectImplem2() + + + with self.assertRaisesRegex(TypeError, 'comparison status has to be a signed integer'): + + s = obj2 < other + + + class BadComparableObjectImplem3a(GObject.Object, ComparableObject): + + def _compare(self, other): + return 123 + + class BadComparableObjectImplem3b(BadComparableObjectImplem3a, ComparableObject): + + def _compare(self, other): + return self.parent_compare() + + obj3 = BadComparableObjectImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + s = obj3 < other + + + class BadComparableObjectImplem4(GObject.Object, ComparableObject): + + def _compare(self, other): + raise Exception('error') + + obj4 = BadComparableObjectImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + s = obj4 < other diff --git a/tests/glibext/configuration.py b/tests/glibext/configuration.py deleted file mode 100644 index 880b445..0000000 --- a/tests/glibext/configuration.py +++ /dev/null @@ -1,106 +0,0 @@ - -import gi -gi.require_version('Gdk', '3.0') -from gi.repository import Gdk - -from chrysacase import ChrysalideTestCase -from pychrysalide.glibext import ConfigParam, GenConfig - - -class TestConfiguration(ChrysalideTestCase): - """TestCase for configuration related items.*""" - - - def testCfgParamValues(self): - """Set and unset configuration parameter values.""" - - color = Gdk.RGBA() - color.parse('#3465A4') - - param = ConfigParam('config.color', ConfigParam.ConfigParamType.COLOR, color) - - self.assertEqual(param.value, color) - - param.make_empty() - - void = Gdk.RGBA(red=0, green=0, blue=0, alpha=0) - self.assertEqual(param.value, void) - - param.value = color - - self.assertEqual(param.value, color) - - - def testCfgParamStates(self): - """Validate all states of an evolving parameter.""" - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER) - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT) - - param.make_empty() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.DEFAULT) - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER, 0x123) - - self.assertEqual(param.value, 0x123) - - self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT) - - param.make_empty() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.EMPTY | ConfigParam.ConfigParamState.CHANGED) - - param.value = 0x1 - - self.assertEqual(param.state, ConfigParam.ConfigParamState.CHANGED) - - param.reset() - - self.assertEqual(param.state, ConfigParam.ConfigParamState.DEFAULT) - - - def testCfgParamDesc(self): - """Export types and states as strings when needed.""" - - param = ConfigParam('config.int', ConfigParam.ConfigParamType.INTEGER) - - self.assertTrue('|' in str(param.state)) - - self.assertTrue('.' in str(param.type)) - - - def testConfiguration(self): - """Feed and browse a basic configuration.""" - - config = GenConfig() - self.assertIsNotNone(config) - self.assertIsNone(config.filename) - - for i in range(5): - param = ConfigParam('config.int.%u' % i, ConfigParam.ConfigParamType.INTEGER, i) - config.add(param) - - chain = '' - - for p in config.params: - chain += '%u' % p.value - - self.assertTrue(chain == ''.join([ '%u' % i for i in range(5) ])) - - found = config.search('config.int.3') - self.assertTrue(found.value == 3) - - found = config.search('config.int.33') - self.assertIsNone(found) - - for p in config.params: - p.value *= 10 - - chain = '' - - for p in config.params: - chain += '%u' % p.value - - self.assertTrue(chain == ''.join([ '%u' % (i * 10) for i in range(5) ])) diff --git a/tests/glibext/hashable.py b/tests/glibext/hashable.py new file mode 100644 index 0000000..07f22e3 --- /dev/null +++ b/tests/glibext/hashable.py @@ -0,0 +1,190 @@ + +import gi + +from chrysacase import ChrysalideTestCase +from gi.repository import GObject +from pychrysalide.glibext import HashableObject + + +class TestStringBuilder(ChrysalideTestCase): + """Test cases for pychrysalide.glibext.HashableObject.""" + + + def testHashableObjectCreation(self): + """Create objects with HashableObject interface.""" + + with self.assertRaisesRegex(NotImplementedError, 'HashableObject can not be constructed'): + + ho = HashableObject() + + + class NewHashableObjectImplem(gi._gi.GObject, HashableObject): + pass + + nho = NewHashableObjectImplem() + + self.assertIsNotNone(nho) + + + class NewHashableObjectImplem2(GObject.Object, HashableObject): + pass + + nho2 = NewHashableObjectImplem() + + self.assertIsNotNone(nho2) + + + def testHashableObjectMethods(self): + """Test the HashableObject methods.""" + + class BasicHashableObjectImplem(gi._gi.GObject, HashableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _hash(self): + return self._val + + value = 1234 + + ho = BasicHashableObjectImplem(value) + + self.assertEqual(hash(ho), value) + + + class BasicHashableObjectImplem2(GObject.Object, HashableObject): + + def __init__(self, val): + super().__init__() + self._val = val + + def _hash(self): + return self._val + + value = 5678 + + ho2 = BasicHashableObjectImplem2(value) + + self.assertEqual(hash(ho2), value) + + + def testCascadingHashableObjectImplementations(self): + """Request the hash from the object parent for a full computing.""" + + + class CascadingHashableObjectFullImplemA(GObject.Object, HashableObject): + + def _hash(self): + return 1 + + class CascadingHashableObjectFullImplemB(CascadingHashableObjectFullImplemA, HashableObject): + + def _hash(self): + return super()._hash() * 2 + + class CascadingHashableObjectFullImplemC(CascadingHashableObjectFullImplemB, HashableObject): + + def _hash(self): + return super()._hash() * 3 + + + obj_a = CascadingHashableObjectFullImplemA() + + obj_b = CascadingHashableObjectFullImplemB() + + obj_c = CascadingHashableObjectFullImplemC() + + + self.assertEqual(hash(obj_a), 1) + self.assertEqual(hash(obj_b), 2) + self.assertEqual(hash(obj_c), 6) + + + class CascadingHashableObjectPartialImplemA(GObject.Object, HashableObject): + + def _hash(self): + return 1 + + class CascadingHashableObjectPartialImplemB(CascadingHashableObjectPartialImplemA): + pass + + class CascadingHashableObjectPartialImplemC(CascadingHashableObjectPartialImplemB, HashableObject): + + def _hash(self): + return super()._hash() * 3 + + + obj_a = CascadingHashableObjectPartialImplemA() + + obj_b = CascadingHashableObjectPartialImplemB() + + obj_c = CascadingHashableObjectPartialImplemC() + + + self.assertEqual(hash(obj_a), 1) + self.assertEqual(hash(obj_b), 1) + self.assertEqual(hash(obj_c), 3) + + + def testHashableObjectExceptions(self): + """Raise exceptions from the HashableObject interface as expected.""" + + class BadHashableObjectImplem(GObject.Object, HashableObject): + pass + + obj = BadHashableObjectImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_hash'"): + + h = hash(obj) + + + with self.assertRaisesRegex(TypeError, 'object parent does not implement the HashableObject interface'): + + h = obj.parent_hash() + + + class BadHashableObjectImplem2(GObject.Object, HashableObject): + + def _hash(self): + return 'AAA' + + obj2 = BadHashableObjectImplem2() + + + with self.assertRaisesRegex(TypeError, 'computed hash value has to be an unsigned integer'): + + h = hash(obj2) + + + class BadHashableObjectImplem3a(GObject.Object, HashableObject): + + def _hash(self): + return 123 + + class BadHashableObjectImplem3b(BadHashableObjectImplem3a, HashableObject): + + def _hash(self): + return self.parent_hash() + + obj3 = BadHashableObjectImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + h = hash(obj3) + + + class BadHashableObjectImplem4(GObject.Object, HashableObject): + + def _hash(self): + raise Exception('error') + + obj4 = BadHashableObjectImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + h = hash(obj4) diff --git a/tests/glibext/singleton.py b/tests/glibext/singleton.py index 4588ae5..712aece 100644 --- a/tests/glibext/singleton.py +++ b/tests/glibext/singleton.py @@ -1,21 +1,24 @@ +import gi + from chrysacase import ChrysalideTestCase from gi.repository import GObject -from pychrysalide.glibext import SingletonCandidate, SingletonFactory +from pychrysalide.glibext import ComparableObject, HashableObject, SingletonCandidate, SingletonFactory class TestSingleton(ChrysalideTestCase): """Test cases for pychrysalide.glibext.SingletonFactory.""" - def testSingletonCreation(self): - """Create singleton objects.""" + def testSingletonCandidateCreation(self): + """Create objects with SingletonCandidate interface.""" with self.assertRaisesRegex(NotImplementedError, 'SingletonCandidate can not be constructed'): sc = SingletonCandidate() - class NewSingletonImplem(GObject.Object, SingletonCandidate): + + class NewSingletonImplem(gi._gi.GObject, HashableObject, ComparableObject, SingletonCandidate): pass nsi = NewSingletonImplem() @@ -23,121 +26,132 @@ class TestSingleton(ChrysalideTestCase): self.assertIsNotNone(nsi) - def testFactoryCreation(self): - """Create singleton factories.""" + class NewSingletonImplem2(GObject.Object, HashableObject, ComparableObject, SingletonCandidate): + pass + + nsi2 = NewSingletonImplem2() - sf = SingletonFactory() + self.assertIsNotNone(nsi2) - self.assertIsNotNone(sf) - class MyFactory(SingletonFactory): - pass + # def testFactoryCreation(self): + # """Create singleton factories.""" + + # sf = SingletonFactory() + + # self.assertIsNotNone(sf) + + # class MyFactory(SingletonFactory): + # pass + + # msf = MyFactory() + + # self.assertIsNotNone(msf) + - msf = MyFactory() - self.assertIsNotNone(msf) - def testSingletonMethods(self): - """Test the singleton methods.""" + # def testSingletonMethods(self): + # """Test the singleton methods.""" - class IntegerCacheImplem(GObject.Object, SingletonCandidate): + # class IntegerCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, val): - super().__init__() - self._val = val + # def __init__(self, val): + # super().__init__() + # self._val = val - def _list_inner_instances(self): - return () + # def _list_inner_instances(self): + # return () - def __hash__(self): - return hash('common-key') + # def __hash__(self): + # return hash('common-key') - def __eq__(self, other): - return self._val == other._val + # def __eq__(self, other): + # return self._val == other._val - val_0 = IntegerCacheImplem(0) - val_0_bis = IntegerCacheImplem(0) - val_1 = IntegerCacheImplem(1) + # val_0 = IntegerCacheImplem(0) + # val_0_bis = IntegerCacheImplem(0) + # val_1 = IntegerCacheImplem(1) - self.assertEqual(hash(val_0), hash(val_0_bis)) - self.assertEqual(hash(val_0), hash(val_1)) + # self.assertEqual(hash(val_0), hash(val_0_bis)) + # self.assertEqual(hash(val_0), hash(val_1)) - self.assertEqual(val_0.hash(), val_0_bis.hash()) - self.assertEqual(val_0.hash(), val_1.hash()) + # self.assertEqual(val_0.hash(), val_0_bis.hash()) + # self.assertEqual(val_0.hash(), val_1.hash()) - self.assertTrue(val_0 == val_0_bis) - self.assertFalse(val_0 == val_1) + # self.assertTrue(val_0 == val_0_bis) + # self.assertFalse(val_0 == val_1) - def testSingletonFootprint(self): - """Check for singleton memory footprint.""" + # def testSingletonFootprint(self): + # """Check for singleton memory footprint.""" - sf = SingletonFactory() + # sf = SingletonFactory() - class IntegerCacheImplem(GObject.Object, SingletonCandidate): + # class IntegerCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, val): - super().__init__() - self._val = val + # def __init__(self, val): + # super().__init__() + # self._val = val - def _list_inner_instances(self): - return () + # def _list_inner_instances(self): + # return () - def __hash__(self): - return hash('common-key') + # def __hash__(self): + # return hash('common-key') - def __eq__(self, other): - return self._val == other._val + # def __eq__(self, other): + # return self._val == other._val - def _set_read_only(self): - pass + # def _set_read_only(self): + # pass - val_0 = IntegerCacheImplem(0) - val_0_bis = IntegerCacheImplem(0) - val_1 = IntegerCacheImplem(1) + # val_0 = IntegerCacheImplem(0) + # val_0_bis = IntegerCacheImplem(0) + # val_1 = IntegerCacheImplem(1) - obj = sf.get_instance(val_0) + # obj = sf.get_instance(val_0) - self.assertTrue(obj is val_0) + # self.assertTrue(obj is val_0) - obj = sf.get_instance(val_0_bis) + # obj = sf.get_instance(val_0_bis) - self.assertTrue(obj is val_0) + # self.assertTrue(obj is val_0) - obj = sf.get_instance(val_1) + # obj = sf.get_instance(val_1) - self.assertTrue(obj is val_1) + # self.assertTrue(obj is val_1) - self.assertEqual(len(obj.inner_instances), 0) + # self.assertEqual(len(obj.inner_instances), 0) - class MasterCacheImplem(GObject.Object, SingletonCandidate): + # class MasterCacheImplem(GObject.Object, SingletonCandidate): - def __init__(self, children): - super().__init__() - self._children = children + # def __init__(self, children): + # super().__init__() + # self._children = children - def _list_inner_instances(self): - return self._children + # def _list_inner_instances(self): + # return self._children - def _update_inner_instances(self, instances): - self._children = instances + # def _update_inner_instances(self, instances): + # self._children = instances - def __hash__(self): - return hash('master-key') + # def __hash__(self): + # return hash('master-key') - def __eq__(self, other): - return False + # def __eq__(self, other): + # return False - def _set_read_only(self): - pass + # def _set_read_only(self): + # pass - master = MasterCacheImplem(( val_0_bis, val_1 )) + # master = MasterCacheImplem(( val_0_bis, val_1 )) - obj = sf.get_instance(master) + # obj = sf.get_instance(master) - self.assertTrue(obj.inner_instances[0] is val_0) + # self.assertTrue(obj.inner_instances[0] is val_0) - self.assertTrue(obj.inner_instances[1] is val_1) + # self.assertTrue(obj.inner_instances[1] is val_1) diff --git a/tests/glibext/strbuilder.py b/tests/glibext/strbuilder.py index ced405e..72fd5c2 100644 --- a/tests/glibext/strbuilder.py +++ b/tests/glibext/strbuilder.py @@ -1,4 +1,6 @@ +import gi + from chrysacase import ChrysalideTestCase from gi.repository import GObject from pychrysalide.glibext import StringBuilder @@ -8,22 +10,31 @@ class TestStringBuilder(ChrysalideTestCase): """Test cases for pychrysalide.glibext.StringBuilder.""" - def testStringBuilderCreation(self): + def ZZZtestStringBuilderCreation(self): """Create objects with StringBuilder interface.""" with self.assertRaisesRegex(NotImplementedError, 'StringBuilder can not be constructed'): - sc = StringBuilder() + sb = StringBuilder() + - class NewStringBuilderImplem(GObject.Object, StringBuilder): + class NewStringBuilderImplem(gi._gi.GObject, StringBuilder): pass - nsi = NewStringBuilderImplem() + nsb = NewStringBuilderImplem() + + self.assertIsNotNone(nsb) + + + class NewStringBuilderImplem2(GObject.Object, StringBuilder): + pass - self.assertIsNotNone(nsi) + nsb2 = NewStringBuilderImplem() + self.assertIsNotNone(nsb2) - def testStringBuilderMethods(self): + + def ZZZtestStringBuilderMethods(self): """Test the StringBuilder methods.""" class BasicStringBuilderImplem(GObject.Object, StringBuilder): @@ -42,3 +53,62 @@ class TestStringBuilder(ChrysalideTestCase): self.assertEqual(sb.to_string(), desc) self.assertEqual(str(sb), desc) self.assertEqual(f'{sb}', desc) + + + def testStringBuilderExceptions(self): + """Raise exceptions from the StringBuilder interface as expected.""" + + + class BadStringBuilderImplem(GObject.Object, StringBuilder): + pass + + obj = BadStringBuilderImplem() + + + with self.assertRaisesRegex(NotImplementedError, "method implementation is missing for '_to_string'"): + + s = str(obj) + + + class BadStringBuilderImplem2(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + return 0xcafe + + obj2 = BadStringBuilderImplem2() + + + with self.assertRaisesRegex(TypeError, 'object description has to get provided as an UTF-8 string value'): + + s = str(obj2) + + + class BadStringBuilderImplem3a(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + return 'desc' + + class BadStringBuilderImplem3b(BadStringBuilderImplem3a, StringBuilder): + + def _to_string(self, flags=0): + return self.parent_to_string() + + obj3 = BadStringBuilderImplem3b() + + + with self.assertRaisesRegex(RuntimeError, 'object parent is not a native type'): + + s = str(obj3) + + + class BadStringBuilderImplem4(GObject.Object, StringBuilder): + + def _to_string(self, flags=0): + raise Exception('error') + + obj4 = BadStringBuilderImplem4() + + + with self.assertRaisesRegex(Exception, 'error'): + + s = str(obj4) diff --git a/tools/maint/exportall.inc b/tools/maint/exportall.inc new file mode 100644 index 0000000..f03be3c --- /dev/null +++ b/tools/maint/exportall.inc @@ -0,0 +1,8 @@ + +# source tools/maint/exportall.inc + +# unset PYTHONPATH GSETTINGS_SCHEMA_DIR LD_LIBRARY_PATH + +source tools/maint/pypath.inc +source tools/maint/gsettings.inc +source tools/maint/libpath.inc diff --git a/tools/maint/libpath.inc b/tools/maint/libpath.inc new file mode 100644 index 0000000..19e68d7 --- /dev/null +++ b/tools/maint/libpath.inc @@ -0,0 +1,10 @@ + +# Look for all path containing binaries for local execution + +# source tools/maint/libpath.inc + +LD_LIBRARY_PATH="$PWD/src/.libs:$LD_LIBRARY_PATH" + +find . -type d -name schemas -exec glib-compile-schemas {} \; + +export LD_LIBRARY_PATH=$( find plugins/ -maxdepth 2 -type d -name '.libs' -printf "$PWD/plugins/%P:" | sed 's/:$//' ):"$LD_LIBRARY_PATH" |