diff options
Diffstat (limited to 'plugins/pychrysalide/bindings.c')
-rw-r--r-- | plugins/pychrysalide/bindings.c | 428 |
1 files changed, 343 insertions, 85 deletions
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); } |