/* Chrysalide - Outil d'analyse de fichiers binaires
 * demangler.c - décodage des noms d'éléments
 *
 * Copyright (C) 2009-2017 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "demangler.h"


#include <malloc.h>
#include <string.h>


//#include "itanium.h"
#include "java.h"



/* Indique si une chaîne peut être traitée par le décodeur. */
typedef bool (* can_be_demangled_fc) (const char *);

/* Prépare de quoi effectuer un décodage. */
typedef GDemanglingContext * (* create_context_fc) (void);

/* Procède au décodage d'une chaîne de caractères. */
typedef bool (* demangle_fc) (GDemanglingContext *, const char *);



/* Appels liés à un décodeur */
typedef struct _demangling_properties
{
    can_be_demangled_fc can_demangle;       /* Possibilité de traitement   */

    create_context_fc create_context;       /* Création de contextes       */

    demangle_fc demangle_routine;           /* Décodage d'une routine      */
    demangle_fc demangle_type;              /* Décodage d'un type          */

} demangling_properties;


/* Liste des décodeurs */
static demangling_properties demanglers[/*DGT_COUNT*/] = {

    /*
    [DGT_ITANIUM] = {
        .can_demangle = (can_be_demangled_fc)can_be_itanium_demangled,
        .create_context = (create_context_fc)g_itanium_dcontext_new,
        .demangle_routine = (demangle_fc)demangle_itanium_routine,
        .demangle_type = (demangle_fc)NULL
    },
    */
    /*
    [DGT_JAVA] = {
        .can_demangle = (can_be_demangled_fc)NULL,
        .create_context = (create_context_fc)g_java_dcontext_new,
        .demangle_routine = (demangle_fc)NULL,
        .demangle_type = (demangle_fc)demangle_java_type
    }
    */
};



/******************************************************************************
*                                                                             *
*  Paramètres  : desc = chaîne de caractères à décoder.                       *
*                                                                             *
*  Description : Tente de décoder une chaîne de caractères donnée.            *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBinRoutine *try_to_demangle_routine(const char *desc)
{
    GBinRoutine *result;                    /* Construction à remonter     */

    result = g_binary_routine_new();
    g_binary_routine_set_name(result, strdup(desc));

    return result;


#if 0
    GBinRoutine *result;                    /* Construction à remonter     */
    DemanglerType i;                        /* Boucle de parcours          */

    static int counter = 0;

    result = NULL;

    if (strcmp(desc, "_ZN21IUDFSettingsValidator15IdNotIllegalStdEN13UDFParameters12UDF_STANDARDES1_") == 0
        || strcmp(desc, "_ZSt12partial_sortIN9__gnu_cxx17__normal_iteratorIP28CPR_MAI_ADPTY_SectorSequenceSt6vectorIS2_SaIS2_EEEEEvT_S8_S8_") == 0
        || strcmp(desc, "_ZSt22__merge_without_bufferIN9__gnu_cxx17__normal_iteratorIP15CProfStringListSt6vectorIS2_SaIS2_EEEEiEvT_S8_S8_T0_S9_") == 0
        || strcmp(desc, "_ZSt11__push_heapIN9__gnu_cxx17__normal_iteratorIP8DRIVE_IDSt6vectorIS2_SaIS2_EEEEiS2_EvT_T0_S9_T1_") == 0    // Intéressant
        //|| strcmp(desc, "") == 0
        )
        goto exit;

    for (i = 0; i < DGT_COUNT; i++)
    {
        if (demanglers[i].can_demangle == NULL)
            continue;

        if (!demanglers[i].can_demangle(desc))
            continue;

        printf("++ [%d] routine :: %s\n", ++counter, desc);
        fflush(NULL);

        result = demangle_routine(i, desc);

        /* FIXME : à supprimer quand mature */
        if (result == NULL)
        {
            printf("++failed :: %s\n", desc);

            if (strcmp(desc, "_ZN21IUDFSettingsValidator15IdNotIllegalStdEN13UDFParameters12UDF_STANDARDES1_") != 0
                && strcmp(desc, "_ZSt12partial_sortIN9__gnu_cxx17__normal_iteratorIP28CPR_MAI_ADPTY_SectorSequenceSt6vectorIS2_SaIS2_EEEEEvT_S8_S8_") != 0
                )
                exit(-1);

        }
        else printf(" -->> '%s'\n\n", g_binary_routine_get_name(result));

        if (result != NULL) break;

    }

 exit:

    if (result == NULL)
    {
        result = g_binary_routine_new();
        g_binary_routine_set_name(result, strdup(desc));
    }

    return result;
#endif

    return NULL;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : type = type de décodeur à utiliser.                          *
*                desc = chaîne de caractères à décoder.                       *
*                                                                             *
*  Description : Tente de décoder une chaîne de caractères donnée.            *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GBinRoutine *demangle_routine(GType type, const char *desc)
{
    GBinRoutine *result;                    /* Construction à remonter     */
    GDemanglingContext *context;            /* Contexte pour le décodage   */

    context = g_object_new(type, NULL);

    result = g_demangling_context_get_decoded_routine(context, desc);

    g_object_unref(context);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : type = type de décodeur à utiliser.                          *
*                desc = chaîne de caractères à décoder.                       *
*                                                                             *
*  Description : Tente de décoder une chaîne de caractères donnée.            *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDataType *demangle_type(GType type, const char *desc)
{
    GBinRoutine *result;                    /* Construction à remonter     */
    GDemanglingContext *context;            /* Contexte pour le décodage   */

    context = g_object_new(type, NULL);

    result = g_demangling_context_get_decoded_type(context, desc);

    g_object_unref(context);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : -                                                            *
*                                                                             *
*  Description : Procède au test de décodages de chaînes de caractères.       *
*                                                                             *
*  Retour      : Bilan de l'opération.                                        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/
#if 0
void test_itanium_demangling(void)
{
    GBinRoutine *routine;
    char *human;
    char stop;

#define TEST_ITANIUM_MANGLING(name, ref)                            \
    do                                                              \
    {                                                               \
        printf(" >> %s\n", name);                                   \
        routine = demangle_routine(DGT_ITANIUM, name);              \
        if (routine == NULL)                                        \
        {                                                           \
            printf("Error with %s\n", name);                        \
            stop = true;                                            \
        }                                                           \
        else                                                        \
        {                                                           \
            human = g_binary_routine_to_string(routine);            \
            printf(" >> %s\n", human);                              \
            stop = (strcmp(ref, human) != 0);                       \
            printf(" >> ok ? %s\n", (!stop ? "oui" : "non"));       \
            if (stop) printf(" >> expected '%s'\n", ref);           \
            free(human);                                            \
        }                                                           \
        if (stop) goto end_of_test;                                 \
        else printf("\n");                                          \
    }                                                               \
    while (0)

    //goto last;

    /**
     * Tests de :
     * http://www.codesourcery.com/public/cxx-abi/abi-examples.html#mangling
     */

    TEST_ITANIUM_MANGLING("_Z1fv", "??? f(void)");

    TEST_ITANIUM_MANGLING("_Z1fi", "??? f(int)");

    TEST_ITANIUM_MANGLING("_Z3foo3bar", "??? foo(bar)");

    TEST_ITANIUM_MANGLING("_Zrm1XS_", "??? %(X, X)");

    TEST_ITANIUM_MANGLING("_ZplR1XS0_", "??? +(X &, X &)");

    TEST_ITANIUM_MANGLING("_ZlsRK1XS1_", "??? <<(const X &, const X &)");

    TEST_ITANIUM_MANGLING("_Z1fIiEvi", "void f<int>(int)");

    TEST_ITANIUM_MANGLING("_Z5firstI3DuoEvS0_", "void first<Duo>(Duo)");

    TEST_ITANIUM_MANGLING("_Z5firstI3DuoEvT_", "void first<Duo>(Duo)");

    TEST_ITANIUM_MANGLING("_Z3fooIiPFidEiEvv", "void foo<int, int (*)(double), int>(void)");

    TEST_ITANIUM_MANGLING("_ZN6System5Sound4beepEv", "??? System::Sound::beep(void)");

    TEST_ITANIUM_MANGLING("_Z1fI1XE vPV N1AIT_E1TE", "void f<X>(volatile A<X>::T *)");

    //// TODO :: TEST_ITANIUM_MANGLING("_ZngILi42EE v N1A I XplT_Li2EE E 1TE", "");

    TEST_ITANIUM_MANGLING("_Z4makeI7FactoryiE T_IT0_E v", "Factory<int> make<Factory, int>(void)");

    TEST_ITANIUM_MANGLING("_ZlsRSoRKSs", "??? <<(std::ostream &, const std::string &)");

    //TEST_ITANIUM_MANGLING("", "");

    /**
     * Tests de :
     * http://www.codesourcery.com/public/cxx-abi/abi.html#mangling
     */

    TEST_ITANIUM_MANGLING("_ZN1N1TIiiE2mfES0_IddE", "??? N::T<int, int>::mf(N::T<double, double>)");

    /**
     * Tests de :
     * (nero.so).
     */

    TEST_ITANIUM_MANGLING("_ZNSt6vectorItSaItEE6insertEN9__gnu_cxx17__normal_iteratorIPtS1_EERKt", "??? std::vector<unsigned short, std::allocator<unsigned short>>::insert(__gnu_cxx::__normal_iterator<unsigned short *, std::vector<unsigned short, std::allocator<unsigned short>>>, const unsigned short &)");

    TEST_ITANIUM_MANGLING("_ZSt26__uninitialized_fill_n_auxIP15CProfStringListiS0_ET_S2_T0_RKT1_12__false_type", "CProfStringList *std::__uninitialized_fill_n_aux<CProfStringList *, int, CProfStringList>(CProfStringList *, int, const CProfStringList &, __false_type)");

    // TODO TEST_ITANIUM_MANGLING("_ZN21IUDFSettingsValidator15IdNotIllegalStdEN13UDFParameters12UDF_STANDARDES1_", "");

    TEST_ITANIUM_MANGLING("_ZNSbI26NeroMediumFeatureSpecifierSt11char_traitsIS_ESaIS_EE4_Rep10_M_destroyERKS2_", "??? std::basic_string<NeroMediumFeatureSpecifier, std::char_traits<NeroMediumFeatureSpecifier>, std::allocator<NeroMediumFeatureSpecifier>>::_Rep::_M_destroy(const std::allocator<NeroMediumFeatureSpecifier> &)");

 last:

    /* 80 */
    TEST_ITANIUM_MANGLING("_ZNSt6vectorIlSaIlEE6insertEN9__gnu_cxx17__normal_iteratorIPlS1_EERKl", "??? std::vector<long, std::allocator<long>>::insert(__gnu_cxx::__normal_iterator<long *, std::vector<long, std::allocator<long>>>, const long &)");

 end_of_test:

    ;

}
#endif