From e1bda768f573b84e340bfcdc59fe353eb665f8e6 Mon Sep 17 00:00:00 2001
From: Cyrille Bagard <nocbos@gmail.com>
Date: Sun, 8 Mar 2020 23:12:42 +0100
Subject: Fixed some mistakes in the Itanium prefixes demangling.

---
 plugins/itanium/abi.c     | 447 ++++++++++++++++++++++++++--------------------
 tests/mangling/itanium.py |  21 +++
 2 files changed, 273 insertions(+), 195 deletions(-)

diff --git a/plugins/itanium/abi.c b/plugins/itanium/abi.c
index 7094aa4..0e7ce27 100644
--- a/plugins/itanium/abi.c
+++ b/plugins/itanium/abi.c
@@ -147,9 +147,6 @@ static itanium_component *itd_nested_name(GItaniumDemangling *);
 static itanium_component *itd_prefix(GItaniumDemangling *);
 
 /* Extrait un composant dans un contexte Itanium. */
-static itanium_component *itd_prefix_rec(GItaniumDemangling *, itanium_component *);
-
-/* Extrait un composant dans un contexte Itanium. */
 static itanium_component *itd_template_prefix(GItaniumDemangling *);
 
 /* Détermine si le composant suivant correspond à un type donné. */
@@ -215,6 +212,9 @@ static itanium_component *itd_template_param(GItaniumDemangling *);
 /* Extrait un composant dans un contexte Itanium. */
 static itanium_component *itd_template_template_param(GItaniumDemangling *);
 
+/* Détermine si le composant suivant correspond à un type donné. */
+static bool is_itd_template_args(GItaniumDemangling *);
+
 /* Extrait un composant dans un contexte Itanium. */
 static itanium_component *itd_template_args(GItaniumDemangling *);
 
@@ -231,6 +231,9 @@ static itanium_component *itd_value_to_string(GItaniumDemangling *, bool);
 static itanium_component *itd_expr_primary(GItaniumDemangling *);
 
 /* Extrait un composant dans un contexte Itanium. */
+static itanium_component *itd_data_member_prefix(GItaniumDemangling *);
+
+/* Extrait un composant dans un contexte Itanium. */
 static bool itd_seq_id(GItaniumDemangling *, char, size_t *);
 
 /* Extrait un composant dans un contexte Itanium. */
@@ -643,6 +646,13 @@ static itanium_component *itd_nested_name(GItaniumDemangling *context)
 
     if (left != NULL)
     {
+        /**
+         * Quand son traitement est un succès, <template-prefix> doit toujours
+         * se terminer par <template-args>.
+         */
+
+        assert(is_itd_template_args(context));
+
         right = itd_template_args(context);
 
         if (right != NULL)
@@ -728,86 +738,98 @@ static itanium_component *itd_prefix(GItaniumDemangling *context)
     input_buffer *ibuf;                     /* Tampon de texte manipulé    */
     char peek;                              /* Prochain caractère lu       */
     itanium_component *targs;               /* Composant 'template-args'   */
+    bool backup;                            /* Solution de repli existante */
+    bool merge_targs;                       /* Fusion nécessaire ?         */
     itd_state saved;                        /* Position d'analyse courante */
     itanium_component *further;             /* Tentative de progression    */
 
     /**
      * La règle traitée ici est la suivante :
      *
-     * <prefix> ::= <prefix> <unqualified-name>
+     * <prefix> ::= <unqualified-name>
+     *          ::= <prefix> <unqualified-name>
      *          ::= <template-prefix> <template-args>
      *          ::= <template-param>
-     *          ::= # empty
+     *          ::= <decltype>
+     *          ::= <prefix> <data-member-prefix>
      *          ::= <substitution>
      *
-     * On note déjà la jolie petite boucle interne.
+     * Réorganisée, cette règle <prefix> a pour définition :
+     *
+     * <prefix> ::= <unqualified-name>
+     *          ::= <template-param>
+     *          ::= <decltype>
+     *          ::= <substitution>
+     *          ::= <template-prefix> <template-args>
      *
-     * Or on a également la règle voisine suivante :
+     *          ::= <prefix> <unqualified-name>
+     *          ::= <prefix> <data-member-prefix>
+     *
+     * Il existe cependant une boucle infinie avec une règle de <prefix> :
      *
      * <template-prefix> ::= <prefix> <template unqualified-name>
-     *                   ::= <template-param>
-     *                   ::= <substitution>
      *
-     * Pour éviter une récursivité qui ferait exploser la pile, on les fusionne
-     * en une nouvelle règle <prefix> étendue :
+     * On déplie donc les règles afin de casser la boucle, quitte à gérer ici
+     * une partie de la règle <template-prefix> :
      *
-     * <prefix> ::= <prefix> <unqualified-name>
-     *          ::= <prefix> <unqualified-name> <template-args>
+     * <prefix> ::= <unqualified-name>
      *          ::= <template-param>
-     *          ::= <template-param> <template-args>
+     *          ::= <decltype>
      *          ::= <substitution>
+     *
+     *          ::= <unqualified-name> <template-args>
+     *          ::= <template-param> <template-args>
      *          ::= <substitution> <template-args>
-     *          ::= # empty
      *
+     *          ::= <prefix> <unqualified-name>
+     *          ::= <prefix> <unqualified-name> <template-args>
+     *          ::= <prefix> <data-member-prefix>
      */
 
-    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;
-
-    if (is_itd_unqualified_name(context))
-        result = itd_unqualified_name(context);
-
-    else
-    {
-        peek = peek_input_buffer_char(ibuf);
-
-        if (peek == 'T')
-            result = itd_template_param(context);
+    result = NULL;
 
-        else if (peek == 'S')
-            result = itd_substitution(context);
+    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;
 
-        else
-            result = NULL;
+    peek = peek_input_buffer_char(ibuf);
 
-    }
+    if (peek == 'S')
+        result = itd_substitution(context);
 
-    if (result == NULL)
-        goto prefix_exit;
+    else if (peek == 'T')
+        result = itd_template_param(context);
 
-    g_itanium_demangling_add_substitution(context, result);
+    else if (is_itd_unqualified_name(context))
+        result = itd_unqualified_name(context);
 
     /**
-     * Détection et traitement de <template-args>.
+     * Si le traitement débouche sur une règle <template-args>, c'est qu'il
+     * était en fait potentiellement dans une voie <template-prefix>.
+     *
+     * Cette voie est vérifiée après coup en analysant la suite.
      */
 
-    peek = peek_input_buffer_char(ibuf);
-
-    if (peek == 'I')
+    if (is_itd_template_args(context))
     {
-        /**
-         * Si la détection est avérée, on vient donc de traiter un cas <template-prefix>.
-         * On corrige donc le type au passage.
-         */
-        if (itd_get_component_type(result) == ICT_PREFIX_BINARY)
-            itd_set_type(result, ICT_TPREFIX_BINARY);
+        g_itanium_demangling_push_state(context, &saved);
 
         targs = itd_template_args(context);
 
         if (targs != NULL)
         {
-            result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);
+            if (!is_itd_unqualified_name(context))
+            {
+                g_itanium_demangling_pop_state(context, &saved);
+                itd_unref_comp(targs);
+            }
 
-            g_itanium_demangling_add_substitution(context, result);
+            else
+            {
+                /* Ajout de la substitution tirée de <template-prefix> */
+                g_itanium_demangling_add_substitution(context, result);
+
+                result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);
+
+            }
 
         }
 
@@ -815,175 +837,118 @@ static itanium_component *itd_prefix(GItaniumDemangling *context)
         {
             itd_unref_comp(result);
             result = NULL;
+            goto done;
         }
 
     }
 
     if (result == NULL)
-        goto prefix_exit;
+        goto done;
 
-    /**
-     * Toutes les règles <prefix> sont suivie par un <[template] unqualified-name>.
-     *
-     * On vérifie donc que c'est le cas.
-     */
-
-    if (!is_itd_unqualified_name(context))
-    {
-        itd_unref_comp(result);
-        result = NULL;
-        goto prefix_exit;
-
-    }
-
-    /**
-     * Cette vérification passée, on peut se trouver dans l'un des cas suivants :
-     *
-     * <prefix> ::= <prefix> <unqualified-name>
-     *          ::= <prefix> <template unqualified-name> <template-args>
-     *
-     * On tente donc une itération supplémentaire.
-     */
-
-    g_itanium_demangling_push_state(context, &saved);
+    g_itanium_demangling_add_substitution(context, result);
 
-    further = itd_prefix_rec(context, result);
+    /* Tentative de rebouclage sur la règle <prefix> */
 
-    if (further != NULL)
-        result = further;
+    while (count_input_buffer_remaining(ibuf) > 0)
+    {
+        backup = is_itd_unqualified_name(context);
 
-    else
-        g_itanium_demangling_pop_state(context, &saved);
+        if (backup)
+            g_itanium_demangling_push_state(context, &saved);
 
- prefix_exit:
+        merge_targs = false;
+        targs = NULL;
 
-    return result;
+        /**
+         * Comme <data-member-prefix> commence par une régle <source-name> et
+         * complète ensuite sa définition et comme <unqualified-name> contient
+         * également cette même règle <source-name> sans l'étendre, on commence
+         * par traiter la règle <data-member-prefix>.
+         */
 
-}
+        further = itd_data_member_prefix(context);
 
+        if (further == NULL)
+        {
+            further = itd_unqualified_name(context);
 
-/******************************************************************************
-*                                                                             *
-*  Paramètres  : context = contexte de décodage à utiliser.                   *
-*                loop    = consigne pour la poursuite de la boucle. [OUT]     *
-*                                                                             *
-*  Description : Extrait un composant dans un contexte Itanium.               *
-*                                                                             *
-*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
-*                                                                             *
-*  Remarques   : -                                                            *
-*                                                                             *
-******************************************************************************/
+            if (further != NULL && is_itd_template_args(context))
+            {
+                merge_targs = true;
+                targs = itd_template_args(context);
 
-static itanium_component *itd_prefix_rec(GItaniumDemangling *context, itanium_component *leaf)
-{
-    itanium_component *result;              /* Construction à retourner    */
-    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
-    char peek;                              /* Prochain caractère lu       */
-    itanium_component *targs;               /* Composant 'template-args'   */
-    itd_state saved;                        /* Position d'analyse courante */
-    itanium_component *further;             /* Tentative de progression    */
+                /* Si l'ensemble ne se termine pas par la règle finale attendue, on rétro-pédale */
+                if (targs != NULL && !is_itd_unqualified_name(context))
+                {
+                    itd_unref_comp(targs);
 
-    /**
-     * La règle locale traitée ici est la suivante :
-     *
-     * <prefix> ::= <prefix> <unqualified-name>
-     *          ::= <prefix> <unqualified-name> <template-args>
-     *
-     * Le premier <prefix> des règles est contenu dans l'arguement leaf.
-     */
+                    merge_targs = false;
+                    targs = NULL;
 
-    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;
+                    g_itanium_demangling_pop_state(context, &saved);
 
-    /**
-     * La présence du <unqualified-name> intial doit être assurée
-     * par l'appelant.
-     *
-     * Mais comme le test ne vaut pas la création, on valide quand même derrière.
-     */
+                    break;
 
-    assert(is_itd_unqualified_name(context));
+                }
 
-    result = itd_unqualified_name(context);
+            }
 
-    if (result == NULL)
-        goto prefix_rec_exit;
+        }
 
-    result = itd_make_binary(ICT_PREFIX_BINARY, leaf, result);
+        if (further == NULL)
+            break;
 
-    g_itanium_demangling_add_substitution(context, result);
+        if (!is_itd_unqualified_name(context))
+        {
+            if (backup)
+                g_itanium_demangling_pop_state(context, &saved);
 
-    /**
-     * Détection et traitement de <template-args>.
-     */
+            itd_unref_comp(further);
 
-    peek = peek_input_buffer_char(ibuf);
+            break;
 
-    if (peek == 'I')
-    {
-        /**
-         * Si la détection est avérée, on vient donc de traiter un cas <template-prefix>.
-         * On corrige donc le type au passage.
-         */
-        if (itd_get_component_type(result) == ICT_PREFIX_BINARY)
-            itd_set_type(result, ICT_TPREFIX_BINARY);
+        }
 
-        targs = itd_template_args(context);
+        result = itd_make_binary(ICT_PREFIX_BINARY, result, further);
 
-        if (targs != NULL)
+        if (merge_targs)
         {
-            result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);
+            if (targs != NULL)
+            {
+                g_itanium_demangling_add_substitution(context, result);
 
-            g_itanium_demangling_add_substitution(context, result);
+                result = itd_make_binary(ICT_TEMPLATE_NAME_ARGS, result, targs);
 
-        }
+            }
+            else
+            {
+                itd_unref_comp(result);
+                result = NULL;
+                goto done;
+            }
 
-        else
-        {
-            itd_ref_comp(leaf);
-            itd_unref_comp(result);
-            result = NULL;
         }
 
+        g_itanium_demangling_add_substitution(context, result);
+
     }
 
-    if (result == NULL)
-        goto prefix_rec_exit;
+ done:
 
     /**
-     * Toutes les règles <prefix> sont suivie par un <[template] unqualified-name>.
-     *
-     * On vérifie donc que c'est le cas.
+     * Quand son traitement est un succès, <prefix> doit toujours
+     * se terminer par <unqualified-name>.
      */
 
-    if (!is_itd_unqualified_name(context))
+    if (result != NULL)
     {
-        itd_ref_comp(leaf);
-        itd_unref_comp(result);
-        result = NULL;
-        goto prefix_rec_exit;
-    }
-
-    /**
-     * Cette vérification passée, on peut se trouver dans l'un des cas suivants :
-     *
-     * <prefix> ::= <prefix> <unqualified-name>
-     *          ::= <prefix> <template unqualified-name> <template-args>
-     *
-     * On tente donc une itération supplémentaire.
-     */
-
-    g_itanium_demangling_push_state(context, &saved);
-
-    further = itd_prefix_rec(context, result);
-
-    if (further != NULL)
-        result = further;
-
-    else
-        g_itanium_demangling_pop_state(context, &saved);
+        if (!is_itd_unqualified_name(context))
+        {
+            itd_unref_comp(result);
+            result = NULL;
+        }
 
- prefix_rec_exit:
+    }
 
     return result;
 
@@ -1005,71 +970,112 @@ static itanium_component *itd_prefix_rec(GItaniumDemangling *context, itanium_co
 static itanium_component *itd_template_prefix(GItaniumDemangling *context)
 {
     itanium_component *result;              /* Construction à retourner    */
+    bool new_subst;                         /* Ajoute d'une substitution ? */
     input_buffer *ibuf;                     /* Tampon de texte manipulé    */
     char peek;                              /* Prochain caractère lu       */
-    itanium_component *prefix;              /* Premier d'un couple de comp.*/
+    itd_state saved;                        /* Position d'analyse courante */
     itanium_component *name;                /* Second d'un couple de comp. */
 
     /**
      * La règle traitée ici est la suivante :
      *
-     * <template-prefix> ::= <prefix> <template unqualified-name>
+     * <template-prefix> ::= <template unqualified-name>
+     *                   ::= <prefix> <template unqualified-name>
      *                   ::= <template-param>
      *                   ::= <substitution>
      *
+     * Il existe cependant une boucle infinie avec une règle de <prefix> :
+     *
+     * <prefix> ::= <template-prefix> <template-args>
+     *
+     * On traite donc cette règle de <prefix> en dernier, quand les autres
+     * options ont été épuisées.
      */
 
+    result = NULL;
+
+    new_subst = true;
+
     ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;
 
     peek = peek_input_buffer_char(ibuf);
 
+    g_itanium_demangling_push_state(context, &saved);
+
     if (peek == 'S')
+    {
         result = itd_substitution(context);
+        new_subst = false;
+    }
 
     else if (peek == 'T')
-    {
         result = itd_template_param(context);
 
-        if (result != NULL)
-            g_itanium_demangling_add_substitution(context, result);
+    if (is_itd_unqualified_name(context))
+        result = itd_unqualified_name(context);
 
-    }
+    /* Vérification : a-t-on empiété sur une règle <prefix> ? */
 
-    else
+    if (result != NULL)
     {
-        prefix = itd_prefix(context);
+        if (is_itd_template_args(context))
+            goto done;
+
+        else
+        {
+            new_subst = true;
 
+            itd_unref_comp(result);
+            result = NULL;
+
+            g_itanium_demangling_pop_state(context, &saved);
+
+        }
+
+    }
+
+    /* Tentative avec <prefix> <template unqualified-name> en dernier recours */
+
+    result = itd_prefix(context);
+
+    if (result != NULL)
+    {
         /**
          * Quand son traitement est un succès, <prefix> doit toujours
          * se terminer par <unqualified-name>.
+         *
+         * De même, toutes les règles <template-prefix> se poursuivent avec une
+         * règle <template-args> ; la procédure n'est donc un succès que dans ce cas.
          */
 
-        assert(prefix == NULL || (prefix != NULL && is_itd_unqualified_name(context)));
-
-        /**
-         * Par ailleurs, la règle <prefix> peut être vide, donc on se doit
-         * de tenter un <unqualified-name> dans tous les cas.
-         */
+        assert(is_itd_unqualified_name(context));
 
         name = itd_unqualified_name(context);
 
         if (name != NULL)
-        {
-            result = itd_make_binary(ICT_TPREFIX_BINARY, prefix, name);
-
-            g_itanium_demangling_add_substitution(context, result);
-
-        }
+            result = itd_make_binary(ICT_TPREFIX_BINARY, result, name);
 
         else
         {
+            itd_unref_comp(result);
             result = NULL;
+        }
 
-            if (prefix != NULL)
-                itd_unref_comp(prefix);
+    }
 
+ done:
+
+    if (result != NULL)
+    {
+        if (!is_itd_template_args(context))
+        {
+            itd_unref_comp(result);
+            result = NULL;
         }
 
+        else if (new_subst)
+            g_itanium_demangling_add_substitution(context, result);
+
     }
 
     return result;
@@ -2699,6 +2705,40 @@ static itanium_component *itd_template_template_param(GItaniumDemangling *contex
     return result;
 
 }
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : context = contexte de décodage à utiliser.                   *
+*                                                                             *
+*  Description : Détermine si le composant suivant correspond à un type donné.*
+*                                                                             *
+*  Retour      : true si le décodage va à priori réussir, false sinon.        *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
+
+static bool is_itd_template_args(GItaniumDemangling *context)
+{
+    bool result;                            /* Bilan à retourner           */
+    input_buffer *ibuf;                     /* Tampon de texte manipulé    */
+    char peek;                              /* Prochain caractère lu       */
+
+    /**
+     * La règle anticipée ici est la suivante :
+     *
+     * <template-args> ::= I <template-arg>+ E
+     *
+     */
+
+    ibuf = &G_DEMANGLING_CONTEXT(context)->buffer;
+
+    peek = peek_input_buffer_char(ibuf);
+
+    result = (peek == 'I');
+
+    return result;
+
+}
 
 
 /******************************************************************************
@@ -3183,10 +3223,27 @@ static itanium_component *itd_expr_primary(GItaniumDemangling *context)
 }
 
 
+/******************************************************************************
+*                                                                             *
+*  Paramètres  : context = contexte de décodage à utiliser.                   *
+*                                                                             *
+*  Description : Extrait un composant dans un contexte Itanium.               *
+*                                                                             *
+*  Retour      : Composant extrait ou NULL en cas d'échec.                    *
+*                                                                             *
+*  Remarques   : -                                                            *
+*                                                                             *
+******************************************************************************/
 
+static itanium_component *itd_data_member_prefix(GItaniumDemangling *context)
+{
+    itanium_component *result;              /* Construction à retourner    */
 
+    result = NULL;
 
+    return result;
 
+}
 
 
 /******************************************************************************
diff --git a/tests/mangling/itanium.py b/tests/mangling/itanium.py
index 6808a99..57a1129 100644
--- a/tests/mangling/itanium.py
+++ b/tests/mangling/itanium.py
@@ -177,6 +177,24 @@ class TestItaniumMangling(ChrysalideTestCase):
         self.check_demangling(demangled, 'void std::partial_sort<__gnu_cxx::__normal_iterator<CPR_MAI_ADPTY_SectorSequence *, std::vector<CPR_MAI_ADPTY_SectorSequence, std::allocator<CPR_MAI_ADPTY_SectorSequence>>>>(__gnu_cxx::__normal_iterator<CPR_MAI_ADPTY_SectorSequence *, std::vector<CPR_MAI_ADPTY_SectorSequence, std::allocator<CPR_MAI_ADPTY_SectorSequence>>>, __gnu_cxx::__normal_iterator<CPR_MAI_ADPTY_SectorSequence *, std::vector<CPR_MAI_ADPTY_SectorSequence, std::allocator<CPR_MAI_ADPTY_SectorSequence>>>, __gnu_cxx::__normal_iterator<CPR_MAI_ADPTY_SectorSequence *, std::vector<CPR_MAI_ADPTY_SectorSequence, std::allocator<CPR_MAI_ADPTY_SectorSequence>>>)')
 
 
+    def testPrefixLoop(self):
+        """Handle the loop between prefixes defined in the specifications."""
+
+        demangler = ItaniumDemangler()
+
+        demangled = demangler.decode_routine('_ZN2aaIN4ccccIfEEE5dddddEj')
+        self.check_demangling(demangled, '??? aa<cccc<float>>::ddddd(unsigned int)')
+
+        demangled = demangler.decode_routine('_ZN2aaIN4cccc4xxxxIfEEE5dddddEj')
+        self.check_demangling(demangled, '??? aa<cccc::xxxx<float>>::ddddd(unsigned int)')
+
+        demangled = demangler.decode_routine('_ZN3zzz2aaIN4cccc4xxxxIfEEE5dddddEj')
+        self.check_demangling(demangled, '??? zzz::aa<cccc::xxxx<float>>::ddddd(unsigned int)')
+
+        demangled = demangler.decode_routine('_ZN3aaa3bbbINS_3cccIfEEE3dddEj')
+        self.check_demangling(demangled, '??? aaa::bbb<ccc<float>>::ddd(unsigned int)')
+
+
     def testAndroidSystem(self):
         """Check Itanium routine demangling from Android system cases."""
 
@@ -187,3 +205,6 @@ class TestItaniumMangling(ChrysalideTestCase):
 
         demangled = demangler.decode_routine('_ZN6icu_556LocaleaSERKS0_')
         self.check_demangling(demangled, '??? icu_55::Locale::operator=(const icu_55::Locale &)')
+
+        demangled = demangler.decode_routine('_ZNSt3__16vectorIfNS_9allocatorIfEEE8__appendEj')
+        self.check_demangling(demangled, '??? std::__1::vector<float, allocator<float>>::__append(unsigned int)')
-- 
cgit v0.11.2-87-g4458