summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac20
-rw-r--r--plugins/pe/core.c15
-rw-r--r--plugins/pychrysalide/analysis/content.c42
-rw-r--r--plugins/pychrysalide/bindings.c428
-rw-r--r--plugins/pychrysalide/bindings.h14
-rw-r--r--plugins/pychrysalide/core-ui.c3
-rw-r--r--plugins/pychrysalide/core.c104
-rw-r--r--plugins/pychrysalide/format/executable.c22
-rw-r--r--plugins/pychrysalide/format/known.c22
-rw-r--r--plugins/pychrysalide/format/program.c20
-rw-r--r--plugins/pychrysalide/glibext/Makefile.am3
-rw-r--r--plugins/pychrysalide/glibext/comparable.c482
-rw-r--r--plugins/pychrysalide/glibext/comparable.h45
-rw-r--r--plugins/pychrysalide/glibext/hashable.c (renamed from plugins/pychrysalide/glibext/comparison.c)251
-rw-r--r--plugins/pychrysalide/glibext/hashable.h (renamed from plugins/pychrysalide/glibext/comparison.h)20
-rw-r--r--plugins/pychrysalide/glibext/module.c6
-rw-r--r--plugins/pychrysalide/glibext/singleton.c342
-rw-r--r--plugins/pychrysalide/glibext/strbuilder.c199
-rw-r--r--plugins/pychrysalide/glibext/strbuilder.h2
-rw-r--r--plugins/pychrysalide/glibext/work.c18
-rw-r--r--plugins/pychrysalide/glibext/workqueue.c6
-rw-r--r--plugins/pychrysalide/helpers.c57
-rw-r--r--plugins/pychrysalide/helpers.h58
-rw-r--r--plugins/pychrysalide/plugins/plugin.c2
-rw-r--r--plugins/pychrysalide/plugins/python.c2
-rw-r--r--src/common/Makefile.am14
-rw-r--r--src/core/core.h8
-rw-r--r--src/glibext/Makefile.am6
-rw-r--r--src/glibext/comparable-int.h47
-rw-r--r--src/glibext/comparable.c110
-rw-r--r--src/glibext/comparable.h46
-rw-r--r--src/glibext/comparison-int.h58
-rw-r--r--src/glibext/comparison.c199
-rw-r--r--src/glibext/comparison.h80
-rw-r--r--src/glibext/hashable-int.h47
-rw-r--r--src/glibext/hashable.c82
-rw-r--r--src/glibext/hashable.h42
-rw-r--r--src/glibext/singleton-int.h17
-rw-r--r--src/glibext/singleton.c179
-rw-r--r--src/glibext/singleton.h14
-rw-r--r--src/glibext/strbuilder-int.h2
-rw-r--r--src/glibext/strbuilder.c9
-rw-r--r--src/glibext/strbuilder.h2
-rw-r--r--src/gtkext/Makefile.am2
-rw-r--r--src/plugins/native-int.h13
-rw-r--r--src/plugins/native.c29
-rw-r--r--src/plugins/native.h7
-rw-r--r--src/plugins/pglist.c146
-rw-r--r--src/plugins/plugin.c41
-rw-r--r--tests/glibext/comparable.py132
-rw-r--r--tests/glibext/configuration.py106
-rw-r--r--tests/glibext/hashable.py190
-rw-r--r--tests/glibext/singleton.py166
-rw-r--r--tests/glibext/strbuilder.py82
-rw-r--r--tools/maint/exportall.inc8
-rw-r--r--tools/maint/libpath.inc10
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"