summaryrefslogtreecommitdiff
path: root/plugins/pychrysalide/bindings.c
diff options
context:
space:
mode:
authorCyrille Bagard <nocbos@gmail.com>2025-01-26 14:25:51 (GMT)
committerCyrille Bagard <nocbos@gmail.com>2025-01-26 14:25:51 (GMT)
commit56c148de74ed8c78ce54ed24daa83ec2f641e054 (patch)
treed4e43da9d6c729146c77fb30de8fa3767b257afb /plugins/pychrysalide/bindings.c
parentb1227a2779c9a72cab1295a1419a9c990df6488e (diff)
Define new interfaces for arch operands.
Diffstat (limited to 'plugins/pychrysalide/bindings.c')
-rw-r--r--plugins/pychrysalide/bindings.c301
1 files changed, 301 insertions, 0 deletions
diff --git a/plugins/pychrysalide/bindings.c b/plugins/pychrysalide/bindings.c
index 99491d6..7e87e27 100644
--- a/plugins/pychrysalide/bindings.c
+++ b/plugins/pychrysalide/bindings.c
@@ -28,6 +28,7 @@
#include <assert.h>
#include <dlfcn.h>
#include <pygobject.h>
+#include <stddef.h>
#include <stdio.h>
@@ -105,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 **);
@@ -504,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. *
* *
@@ -874,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
@@ -1158,6 +1457,8 @@ static bool init_python_interpreter_for_standalone_mode(const pyinit_details_t *
static void PyExit_pychrysalide(void)
{
+ unhook_pygobject_behaviour();
+
exit_all_plugins();
unload_core_components(ACC_ALL_COMPONENTS);