summaryrefslogtreecommitdiff
path: root/plugins/pychrysalide
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/pychrysalide')
-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
23 files changed, 1620 insertions, 528 deletions
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);