diff options
author | Cyrille Bagard <nocbos@gmail.com> | 2025-01-26 14:25:51 (GMT) |
---|---|---|
committer | Cyrille Bagard <nocbos@gmail.com> | 2025-01-26 14:25:51 (GMT) |
commit | 56c148de74ed8c78ce54ed24daa83ec2f641e054 (patch) | |
tree | d4e43da9d6c729146c77fb30de8fa3767b257afb /plugins/pychrysalide/bindings.c | |
parent | b1227a2779c9a72cab1295a1419a9c990df6488e (diff) |
Define new interfaces for arch operands.
Diffstat (limited to 'plugins/pychrysalide/bindings.c')
-rw-r--r-- | plugins/pychrysalide/bindings.c | 301 |
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); |