From 89f802045b71299ba70a8df17ca09d5fcc37cf99 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Fri, 27 May 2022 18:31:43 +0200
Subject: Avoid calls to Python _PyDict_NewKeysForClass() internal function.

---
 plugins/pychrysalide/helpers.c | 129 ++++++++++++++---------------------------
 1 file changed, 45 insertions(+), 84 deletions(-)

diff --git a/plugins/pychrysalide/helpers.c b/plugins/pychrysalide/helpers.c
index b04c801..8e7c10c 100644
--- a/plugins/pychrysalide/helpers.c
+++ b/plugins/pychrysalide/helpers.c
@@ -717,7 +717,7 @@ PyObject *not_yet_implemented_getter(PyObject *self, void *closure)
 
 /******************************************************************************
 *                                                                             *
-*  Paramètres  : spec = définition à mettre en place dynamiquement.           *
+*  Paramètres  : otype = définition à mettre en place dynamiquement.          *
 *                                                                             *
 *  Description : Définit dans le tas de Python un nouveau type.               *
 *                                                                             *
@@ -727,102 +727,63 @@ PyObject *not_yet_implemented_getter(PyObject *self, void *closure)
 *                                                                             *
 ******************************************************************************/
 
-PyTypeObject *define_python_dynamic_type(const PyTypeObject *spec)
+PyTypeObject *define_python_dynamic_type(const PyTypeObject *otype)
 {
     PyTypeObject *result;                   /* Définition créée à renvoyer */
-    PyTypeObject *type;                     /* Type de tous les types      */
-    size_t size;                            /* Taille de la définition     */
-    char *s;                                /* Marqueur de début de chaîne */
-    PyHeapTypeObject *hobj;                 /* Version réelle du type créé */
+    PyType_Slot slots[10];                  /* Emplacements pour infos     */
+    PyType_Spec spec;                       /* Définition du type          */
+    PyType_Slot *iter;                      /* Boucle de parcours          */
+    PyObject *bases;                        /* Bases de construction       */
 
-    /**
-     * Le cahier des charges est ici d'éviter les erreurs suivantes :
-     *
-     *    TypeError: type 'XXX' is not dynamically allocated but its base type 'YYY' is dynamically allocated
-     *    Fatal Python error: unexpected exception during garbage collection
-     *
-     * L'allocation dynamique est marquée par le fanion Py_TPFLAGS_HEAPTYPE.
-     *
-     * Une des rares fonctions qui appliquent ce fanion est PyType_FromSpecWithBases(),
-     * mais elle appelle ensuite PyType_Ready(), ce qui est incompatible avec des modifications
-     * utltérieures avant un appel à pygobject_register_class().
-     *
-     * Le code suivant s'inspire fortement des méthodes originales de Python,
-     * dont les mécanismes employés par PyType_GenericAlloc().
-     */
-
-    type = &PyType_Type;
-    size = _PyObject_SIZE(type);
-
-    if (PyType_IS_GC(type))
-        result = (PyTypeObject *)_PyObject_GC_Malloc(size);
-    else
-        result = (PyTypeObject *)PyObject_MALLOC(size);
-
-    if (type->tp_flags & Py_TPFLAGS_HEAPTYPE)
-        Py_INCREF(type);
-
-    /* Définitions sommaires */
-
-    memset(result, 0, sizeof(PyHeapTypeObject));
-
-    memcpy(result, spec, sizeof(PyTypeObject));
-
-    result->tp_flags |= Py_TPFLAGS_HEAPTYPE;
-
-    /* Définitions des noms */
-
-    /**
-     * Pour un type dynamique, les désignations ne s'appuient pas sur la partie réservée
-     * au type, mais sur les données suivantes (cf. type_name() et type_qualname()).
-     *
-     * Les deux fonctions désignées sont par ailleurs facilement accessibles :
-     *
-     *    #0  0x0000555555689bf0 in type_qualname (...) at Objects/typeobject.c:393
-     *    #1  0x000055555568b3b4 in type_repr (...) at Objects/typeobject.c:855
-     *    #2  0x0000555555693574 in object_str (...) at Objects/typeobject.c:3511
-     *    #3  0x0000555555670d02 in PyObject_Str (...) at Objects/object.c:535
-     *    ...
-     *
-     * On s'inspire donc du contenu de PyType_FromSpecWithBases() pour éviter tout
-     * plantage du fait de données non initialisées.
-     */
+    bases = PyTuple_Pack(1, &PyType_Type);
 
-    hobj = (PyHeapTypeObject *)result;
+    spec.name = otype->tp_name;
+    spec.basicsize = otype->tp_basicsize;
+    spec.flags = otype->tp_flags;
+    spec.slots = slots;
 
-    s = strrchr(spec->tp_name, '.');
-
-    if (s == NULL)
-        s = (char *)spec->tp_name;
-    else
-        s++;
+    iter = &slots[0];
 
-    hobj->ht_name = PyUnicode_FromString(s);
-    assert(hobj->ht_name != NULL);
-
-    hobj->ht_qualname = hobj->ht_name;
-    Py_INCREF(hobj->ht_qualname);
+    if (otype->tp_doc != NULL)
+    {
+        iter->slot = Py_tp_doc;
+        iter->pfunc = (void *)otype->tp_doc;
+        iter++;
+    }
 
-    result->tp_as_async = &hobj->as_async;
-    result->tp_as_number = &hobj->as_number;
-    result->tp_as_sequence = &hobj->as_sequence;
-    result->tp_as_mapping = &hobj->as_mapping;
-    result->tp_as_buffer = &hobj->as_buffer;
+    if (otype->tp_methods != NULL)
+    {
+        iter->slot = Py_tp_methods;
+        iter->pfunc = otype->tp_methods;
+        iter++;
+    }
 
-    hobj->ht_cached_keys = _PyDict_NewKeysForClass();
+    if (otype->tp_getset != NULL)
+    {
+        iter->slot = Py_tp_getset;
+        iter->pfunc = otype->tp_getset;
+        iter++;
+    }
 
+    if (otype->tp_init != NULL)
+    {
+        iter->slot = Py_tp_init;
+        iter->pfunc = otype->tp_init;
+        iter++;
+    }
 
+    if (otype->tp_new != NULL)
+    {
+        iter->slot = Py_tp_new;
+        iter->pfunc = otype->tp_new;
+        iter++;
+    }
 
+    iter->slot = 0;
 
-#if 0
-    if (type->tp_itemsize == 0)
-        (void)PyObject_INIT(result, type);
-    else
-        (void) PyObject_INIT_VAR((PyVarObject *)result, type, 0);
+    result = (PyTypeObject *)PyType_FromSpecWithBases(&spec, bases);
 
-    if (PyType_IS_GC(type))
-        _PyObject_GC_TRACK(result);
-#endif
+    Py_DECREF(bases);
 
     return result;
 
-- 
cgit v0.11.2-87-g4458