/* Chrysalide - Outil d'analyse de fichiers binaires
 * il.h - mise en place d'un langage intermédiaire
 *
 * Copyright (C) 2012-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 "il.h"


#include <malloc.h>


#include "../blocks/flow.h"
#include "../blocks/virtual.h"
#include "../../decomp/expr/block.h"
#include "../../decomp/expr/immediate.h"
#include "../../decomp/instr/ite.h"
#include "../../decomp/instr/keyword.h"
#include "../../decomp/instr/switch.h"



/* --------------------- GESTION DES CONTEXTES DE DECOMPILATION --------------------- */


/* Détermine les registres utilisés avant leur initialisation. */
static bool track_used_registers(GFlowBlock *, BlockFollowPosition, GRAccessList **);

/* Etablit le relévé des allocations de registre. */
static void setup_awaited_regs_allocation(const GInstrBlock *, vmpa_t);

/* Etablit la liste de tous les allocations attendues. */
static bool merge_all_awaited_regs(GInstrBlock *, BlockVisitOrder, GRAccessList *);

/* Met en place un contexte adapté aux sous-blocs d'un bloc. */
static GDecContext *create_new_context_for_sub_block(GDecContext *, GInstrBlock *, GHashTable *);



/* -------------------------- ENCADREMENT DES INSTRUCTIONS -------------------------- */


/* Retrouve et rassemble les instructions décompilées. */
static GDecInstruction *merge_decompiled_instructions(GDecInstruction *, GDecInstruction *);

/* Procède à la décompilation d'un bloc déterminé. */
static GDecInstruction *decompiled_instructions_block(GFlowBlock *, GDecContext *);

/* Procède à la décompilation d'un ensemble de blocs déterminé. */
static GDecInstruction *decompiled_instructions_blocks(GVirtualBlock *, GDecContext *);

/* Procède à la décompilation d'un bloc basique quelconque. */
static GDecInstruction *decompiled_basic_block(GInstrBlock *, GDecContext *);



/* --------------------------- DECOMPILATIONS SPECIFIQUES --------------------------- */


/* Procède à la décompilation des éléments d'un 'if then else'. */
static void build_ite_branches(GITEInstruction *, GFlowBlock *, GDecContext *);

/* Termine le traitement d'un cas de 'switch'. */
static void close_case_decomp_instructions(GDecInstruction *, GInstrBlock *, GArchInstruction **, size_t, size_t);

/* Procède à la décompilation des éléments d'un 'switch'. */
static void build_switch_branches(GSwitchInstruction *, GFlowBlock *, GDecContext *);



/* ---------------------------------------------------------------------------------- */
/*                       GESTION DES CONTEXTES DE DECOMPILATION                       */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : block  = bloc d'instructions visité.                         *
*                pos    = indication sur la position du parcours.             *
*                needed = suivi de l'usage des registres entre les blocs.     *
*                                                                             *
*  Description : Détermine les registres utilisés avant leur initialisation.  *
*                                                                             *
*  Retour      : true pour mener un parcours complet.                         *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool track_used_registers(GFlowBlock *block, BlockFollowPosition pos, GRAccessList **needed)
{
    GRAccessList *old;                      /* Ancienne liste remplacée    */
    const GRAccessList *accesses;           /* Accès divers aux registres  */
    size_t count;                           /* Taille d'un parcours        */
    GRAccessList *awaited;                  /* Satisfactions des besoins   */
    reg_access *access;                     /* Accès à un registre         */
    size_t i;                               /* Boucle de parcours          */
    reg_access *found;                      /* Besoin trouvé ou non        */

    switch (pos)
    {
        case BFP_ENTER:
            g_object_set_data(G_OBJECT(block), "needed_regs", *needed);
            break;

        case BFP_FOLLOW:
            *needed = g_raccess_list_new();
            break;

        case BFP_BACK:

            old = *needed;
            *needed = G_RACCESS_LIST(g_object_get_data(G_OBJECT(block), "needed_regs"));

            g_raccess_list_merge(*needed, old);
            g_object_unref(G_OBJECT(old));

            break;

        case BFP_EXIT:

            g_object_set_data(G_OBJECT(block), "needed_regs", NULL);

            accesses = g_flow_block_list_regs_accesses(block);
            count = g_raccess_list_count(accesses);

            awaited = g_flow_block_list_awaited_regs(block);

            for (i = 0; i < count; i++)
            {
                access = g_raccess_list_get(accesses, i);

                /* Enregistrement des contributions possibles */

                found = g_raccess_list_find(*needed, access->reg);

                if (found != NULL)
                {
                    /**
                     * Si un autre bloc avait déjà un besoin, on ne prend note
                     * qu'une seule fois !
                     */
                    if (g_raccess_list_find(awaited, access->reg) == NULL)
                        g_raccess_list_add(awaited, access);

                    g_raccess_list_delete(*needed, found);

                }

                /* Ajoute les besoins du bloc */
                if (access->first_access == RAT_READ)
                    g_raccess_list_add(*needed, access);

            }

            /*
            do
            {
                vmpa_t start, end;

                g_flow_block_get_boundary_addresses(block, &start, &end);

                printf(" -> flow (%d) : 0x%08x - 0x%08x  |  needed = %zu - provided = %zu\n",
                       pos, start, end,
                       g_raccess_list_count(*needed), g_raccess_list_count(awaited));

            }
            while (0);
            */

            break;

    }

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : list  = ensemble des instructions d'assemblage à traiter.    *
*                start = adresse de départ de la routine visée.               *
*                                                                             *
*  Description : Etablit le relévé des allocations de registre.               *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void setup_awaited_regs_allocation(const GInstrBlock *list, vmpa_t start)
{
    GInstrBlock *first;                     /* Bloc de départ du flot      */
    GRAccessList *needed;                   /* Registres inter-blocs       */

    first = g_instr_block_find_by_addr(list, start, true);

    needed = g_raccess_list_new();

    g_flow_block_follow(G_FLOW_BLOCK(first), list, BFP_ENTER | BFP_EXIT | BFP_EXIT,
                        (flow_block_follow_cb)track_used_registers, &needed);

    g_object_unref(G_OBJECT(needed));

}


/******************************************************************************
*                                                                             *
*  Paramètres  : block = bloc d'instructions concerné par la visite.          *
*                order = position dans la visite.                             *
*                list  = liste à compléter.                                   *
*                                                                             *
*  Description : Etablit la liste de tous les allocations attendues.          *
*                                                                             *
*  Retour      : true pour parcourir tous les blocs.                          *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static bool merge_all_awaited_regs(GInstrBlock *block, BlockVisitOrder order, GRAccessList *list)
{
    const GRAccessList *awaited;            /* Registres conséquents       */

    if (G_IS_FLOW_BLOCK(block))
    {
        awaited = g_flow_block_list_regs_accesses(G_FLOW_BLOCK(block));
        g_raccess_list_merge(list, awaited);
    }

    return true;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : ctx    = contexte de décompilation courant.                  *
*                block  = block regroupant les branches de division.          *
*                shared = liste des allocations passées de registres attendus.*
*                                                                             *
*  Description : Met en place un contexte adapté aux sous-blocs d'un bloc.    *
*                                                                             *
*  Retour      : Nouveau contexte près à emploi.                              *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GDecContext *create_new_context_for_sub_block(GDecContext *ctx, GInstrBlock *block, GHashTable *shared)
{
    GDecContext *result;                    /* Contexte à retourner        */
    GRAccessList *list;                     /* Allocations attendues       */

    result = g_dec_context_dup(ctx);

    list = g_raccess_list_new();
    g_instr_block_visit(block, (instr_block_visitor_cb)merge_all_awaited_regs, list);
    g_dec_context_set_awaited(result, list);
    g_object_unref(G_OBJECT(list));

    g_dec_context_set_shared_allocs(result, shared);

    return result;

}



/* ---------------------------------------------------------------------------------- */
/*                            ENCADREMENT DES INSTRUCTIONS                            */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : group = groupe d'instructions à compléter ou constituer.     *
*                list  = liste d'instructions à intégrer.                     *
*                                                                             *
*  Description : Retrouve et rassemble les instructions décompilées.          *
*                                                                             *
*  Retour      : Groupe fourni ou nouveau groupe créé pour l'occasion.        *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GDecInstruction *merge_decompiled_instructions(GDecInstruction *group, GDecInstruction *list)
{
    GExprBlock *block;                      /* Autre vision du groupe      */
    GDecInstruction *iter;                  /* Boucle de parcours          */

    if (group == NULL) block = NULL;
    else block = G_EXPR_BLOCK(group);

    for (iter = list;
         iter != NULL;
         iter = g_dec_instruction_get_next_iter(list, iter))
    {
        if (block == NULL)
            block = G_EXPR_BLOCK(g_expr_block_new(iter));
        else
            g_expr_block_add_item(block, iter);

    }

    return G_DEC_INSTRUCTION(block);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : block = ensemble des instructions d'assemblage à traiter.    *
*                ctx   = contexte de soutien à associer à l'opération.        *
*                                                                             *
*  Description : Procède à la décompilation d'un bloc déterminé.              *
*                                                                             *
*  Retour      : Instructions créées et enregistrées, ou NULL si erreur.      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GDecInstruction *decompiled_instructions_block(GFlowBlock *block, GDecContext *ctx)
{
    GArchInstruction *instrs;               /* Liste d'instructions natives*/
    GArchInstruction *first;                /* Première instruction du lot */
    vmpa_t max;                             /* Adresse de fin du bloc      */
    GArchInstruction *iter;                 /* Boucle de parcours #1       */
    GDecInstruction *decomp;                /* Dernier résultat de décomp. */

    instrs = NULL; // FIXME g_flow_block_get_all_instructions_list(block);
    g_flow_block_get_boundary(block, &first, NULL);

    g_flow_block_get_boundary_addresses(block, NULL, &max);
    max++;

    /* Décompilation du corps du bloc */

    for (iter = first;
         iter != NULL;
         iter = g_arch_instruction_get_next_iter(instrs, iter, max))
    {
        decomp = g_arch_instruction_decompile(iter, ctx);
    }

    /* Post-traitement selon les types de lien */

    /* if ... then ... else ... */
    if (G_IS_ITE_INSTRUCTION(decomp))
        build_ite_branches(G_ITE_INSTRUCTION(decomp), block, ctx);

    /* switch ... case ... */
    else if (G_IS_SWITCH_INSTRUCTION(decomp))
        build_switch_branches(G_SWITCH_INSTRUCTION(decomp), block, ctx);

    /* Renvoi des instructions mises en place */

    return g_dec_context_get_decomp_instrs(ctx);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : block = ensemble des instructions d'assemblage à traiter.    *
*                ctx   = contexte de soutien à associer à l'opération.        *
*                                                                             *
*  Description : Procède à la décompilation d'un ensemble de blocs déterminé. *
*                                                                             *
*  Retour      : Instructions créées et enregistrées, ou NULL si erreur.      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GDecInstruction *decompiled_instructions_blocks(GVirtualBlock *block, GDecContext *ctx)
{
    GDecInstruction *result;                /* Instructions à renvoyer     */
    size_t count;                           /* Nombre de sous-blocs        */
    size_t i;                               /* Boucle de parcours          */
    GInstrBlock *sub_block;                 /* Sous-bloc à traiter         */
    GDecInstruction *dinstrs;               /* Instructions décompilées    */

    result = NULL;

    count = g_virtual_block_count_children(block);

    for (i = 0; i < count; i++)
    {
        sub_block = g_virtual_block_get_child(block, i);

        /**
         * Les groupes de blocs sont forcément rattachés à une instruction,
         * donc ils sont décompilés depuis ces instructions, pas ici.
         */
        if (!G_IS_FLOW_BLOCK(sub_block)) continue;

        /* FIXME */
        //g_dec_context_set_decomp_instrs(ctx, NULL);

        dinstrs = decompiled_instructions_block(G_FLOW_BLOCK(sub_block), ctx);
        //result = merge_decompiled_instructions(result, dinstrs);

    }

    result = merge_decompiled_instructions(result, dinstrs);

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : block = ensemble des instructions d'assemblage à traiter.    *
*                ctx   = contexte de soutien à associer à l'opération.        *
*                                                                             *
*  Description : Procède à la décompilation d'un bloc basique quelconque.     *
*                                                                             *
*  Retour      : Instructions créées et enregistrées, ou NULL si erreur.      *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static GDecInstruction *decompiled_basic_block(GInstrBlock *block, GDecContext *ctx)
{
    GDecInstruction *result;                /* Instructions à retourner    */

    if (G_IS_VIRTUAL_BLOCK(block))
        result = decompiled_instructions_blocks(G_VIRTUAL_BLOCK(block), ctx);

    else
    {
        result = decompiled_instructions_block(G_FLOW_BLOCK(block), ctx);

        if (!G_IS_EXPR_BLOCK(result))
            result = merge_decompiled_instructions(NULL, result);

    }

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : routine = routine dont le corps est à traiter.               *
*                format  = format du binaire contenant les instructions.      *
*                proc    = architecture du code machine.                      *
*                                                                             *
*  Description : Procède à la décompilation d'une routinée déterminée.        *
*                                                                             *
*  Retour      : Instructions créées ou NULL si erreur.                       *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

GDecInstruction *decompiled_routine_instructions(GBinRoutine *routine, GExeFormat *format, GArchProcessor *proc)
{
    GDecInstruction *result;                /* Instructions à retourner    */
    GDecContext *context;                   /* Contexte pour la décompil.  */
    GInstrBlock *blocks;                    /* Blocs basiques de routine   */

    context = g_arch_processor_get_decomp_context(proc);
    g_dec_context_set_info(context, routine, format);

    blocks = g_binary_routine_get_basic_blocks(routine);

    setup_awaited_regs_allocation(blocks, g_binary_routine_get_address(routine));

    result = decompiled_basic_block(blocks, context);

    g_object_unref(context);

    return result;

}



/* ---------------------------------------------------------------------------------- */
/*                             DECOMPILATIONS SPECIFIQUES                             */
/* ---------------------------------------------------------------------------------- */


/******************************************************************************
*                                                                             *
*  Paramètres  : decomp = instruction 'if ... then ... else' à compléter.     *
*                block  = ensemble des instructions d'assemblage traitées.    *
*                ctx    = contexte de soutien à associer à l'opération.       *
*                                                                             *
*  Description : Procède à la décompilation des éléments d'un 'if then else'. *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void build_ite_branches(GITEInstruction *decomp, GFlowBlock *block, GDecContext *ctx)
{
    GArchInstruction *last;                 /* Dernière instruction du lot */
    GInstrBlock *sub_parent;                /* Groupe des sous-branches    */
    GHashTable *sub_shared;                 /* Allocations communes        */
    GDecContext *sub_ctx;                   /* Sous-contexte pour branche  */
    GDecInstruction *true_dinstr;           /* Décompilation 'cond vraie'  */
    GDecInstruction *false_dinstr;          /* Décompilation 'cond fausse' */
    GArchInstruction *next;                 /* Instruction de branchement  */
    vmpa_t next_addr;                       /* Adresse de cette instruct°  */
    GInstrBlock *next_block;                /* Sous-bloc basique direct    */

    g_flow_block_get_boundary(block, NULL, &last);

    sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block));
    sub_shared = g_hash_table_new_full((GHashFunc)g_arch_register_hash,
                                       (GEqualFunc)NULL,//g_arch_register_equal,
                                       g_object_unref, g_object_unref);

    true_dinstr = NULL;
    false_dinstr = NULL;

    /* Branche 'true' */
    next = g_arch_instruction_get_given_destination(last, ILT_JUMP_IF_TRUE);
    if (next != NULL)
    {
        g_arch_instruction_get_location(next, NULL, NULL, &next_addr);
        next_block = g_instr_block_find_by_addr(sub_parent, next_addr, false);

        if (next_block != NULL)
        {
            sub_ctx = create_new_context_for_sub_block(ctx, next_block, sub_shared);
            true_dinstr = decompiled_basic_block(next_block, sub_ctx);
            g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx);
            g_object_unref(G_OBJECT(sub_ctx));
        }

    }

    /* Branche 'false' */
    next = g_arch_instruction_get_given_destination(last, ILT_JUMP_IF_FALSE);
    if (next != NULL)
    {
        g_arch_instruction_get_location(next, NULL, NULL, &next_addr);
        next_block = g_instr_block_find_by_addr(sub_parent, next_addr, false);

        if (next_block != NULL)
        {
            sub_ctx = create_new_context_for_sub_block(ctx, next_block, sub_shared);
            false_dinstr = decompiled_basic_block(next_block, sub_ctx);
            g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx);
            g_object_unref(G_OBJECT(sub_ctx));
        }

    }

    g_ite_instruction_set_branches(decomp, true_dinstr, false_dinstr);

    g_hash_table_unref(sub_shared);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : case_dinstr = instructions d'un cas de 'switch' décompilées. *
*                case_block  = bloc d'instructions assembleur correspondantes.*
*                cases       = listes des instructions des différents cas.    *
*                current     = indice du cas courant.                         *
*                ccount      = nombre de cas présents dans le 'switch'.       *
*                                                                             *
*  Description : Termine le traitement d'un cas de 'switch'.                  *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void close_case_decomp_instructions(GDecInstruction *case_dinstr, GInstrBlock *case_block, GArchInstruction **cases, size_t current, size_t ccount)
{
    vmpa_t *cases_addr;                     /* Adresses des cas du 'switch'*/
    size_t i;                               /* Boucle de parcours #1       */
    GInstrBlock **leafs;                    /* Blocs terminaux du cas      */
    size_t lcount;                          /* Nombre de blocs terminaux   */
    vmpa_t common_addr;                     /* Adresse commune de suite    */
    bool is_common;                         /* Suite commune ?             */
    GArchInstruction *last;                 /* Dernière instruction de bloc*/
    instr_link_t *dests;                    /* Instr. visées par une autre */
    size_t dcount;                          /* Nombre de liens de dest.    */
    size_t j;                               /* Boucle de parcours #2       */
    vmpa_t addr;                            /* Adresse d'une instruction   */
    bool jump_to_case;                      /* Suite dans le cas suivant ? */

    /* Etablit la liste des adresses des cas */

    cases_addr = (vmpa_t *)calloc(ccount, sizeof(vmpa_t));

    for (i = 0; i < ccount; i++)
        g_arch_instruction_get_location(cases[i], NULL, NULL, &cases_addr[i]);

    /* Récupère la (les) fin(s) du cas présent */

    leafs = NULL;
    lcount = 0;

    g_instr_block_list_leafs_blocks(case_block, &leafs, &lcount);

    /* Procède à une première analyse de la suite */

    common_addr = VMPA_MAX;
    is_common = true;

    for (i = 0; i < lcount && is_common; i++)
    {
        g_flow_block_get_boundary(G_FLOW_BLOCK(leafs[i]), NULL, &last);
        dcount = g_arch_instruction_get_destinations(last, &dests);

        for (j = 0; j < dcount && is_common; j++)
        {
            g_arch_instruction_get_location(dests[j].linked, NULL, NULL, &addr);

            if (common_addr == VMPA_MAX)
                common_addr = addr;
            else
                is_common = (common_addr == addr);

        }

    }

    /* La sortie du cas est unique ! */
    if (is_common)
    {
        if (common_addr == VMPA_MAX)
            goto ecdi_exit;

        jump_to_case = false;

        for (i = 0; i < ccount && !jump_to_case; i++)
            if (i != current)
                jump_to_case = (cases_addr[i] == common_addr);

        if (!jump_to_case)
            g_expr_block_add_item(G_EXPR_BLOCK(case_dinstr), g_keyword_instruction_new(DKW_BREAK));

    }

    /* ... ou il faut suivre les différentes branches... */
    else
    {

        /* TODO */

    }

 ecdi_exit:

    free(cases_addr);

}


/******************************************************************************
*                                                                             *
*  Paramètres  : decomp = instruction 'switch' à compléter.                   *
*                block  = ensemble des instructions d'assemblage traitées.    *
*                ctx    = contexte de soutien à associer à l'opération.       *
*                                                                             *
*  Description : Procède à la décompilation des éléments d'un 'switch'.       *
*                                                                             *
*  Retour      : -                                                            *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

static void build_switch_branches(GSwitchInstruction *decomp, GFlowBlock *block, GDecContext *ctx)
{
#if 0

    GArchInstruction *last;                 /* Dernière instruction du lot */
    GInstrBlock *sub_parent;                /* Groupe des sous-branches    */
    GHashTable *sub_shared;                 /* Allocations communes        */
    GDecContext *sub_ctx;                   /* Sous-contexte pour branche  */
    vmpa_t next_addr;                       /* Adresse de cette instruct°  */
    GInstrBlock *next_block;                /* Sous-bloc basique direct    */
    GArchInstruction **dests;               /* Instr. visée par une autre  */
    link_extra_info *info;                  /* Compléments pour les liens  */
    size_t dcount;                          /* Nombre de liens de dest.    */
    size_t i;                               /* Boucle de parcours #2       */
    GDecInstruction *case_dinstr;           /* Décompilation 'case'        */
    GDecExpression *case_value;             /* Valeur d'aiguillage         */

    g_flow_block_get_boundary(block, NULL, &last);

    sub_parent = g_instr_block_get_links_block(G_INSTR_BLOCK(block));
    sub_shared = g_hash_table_new_full((GHashFunc)g_arch_register_hash,
                                       (GEqualFunc)g_arch_register_equal,
                                       g_object_unref, g_object_unref);

    dcount = g_arch_instruction_get_destinations(last, &dests, NULL, &info);

    for (i = 0; i < dcount; i++)
    {
        g_arch_instruction_get_location(dests[i], NULL, NULL, &next_addr);
        next_block = g_instr_block_find_by_addr(sub_parent, next_addr, false);

        if (next_block != NULL)
        {
            sub_ctx = create_new_context_for_sub_block(ctx, next_block, sub_shared);
            case_dinstr = decompiled_basic_block(next_block, sub_ctx);
            g_dec_context_spread_allocated_shared_regs(ctx, sub_ctx);
            g_object_unref(G_OBJECT(sub_ctx));

            close_case_decomp_instructions(case_dinstr, next_block, dests, i, dcount);
            g_expr_block_set_border_behavior(G_EXPR_BLOCK(case_dinstr), BBB_FORCE_OFF);

            if (info[i].imm != NULL)
            {
                case_value = G_DEC_EXPRESSION(g_imm_expression_new(info[i].imm));
                g_switch_instruction_add_case(G_SWITCH_INSTRUCTION(decomp),
                                              case_value, case_dinstr, next_addr);
            }
            else g_switch_instruction_set_default_case(G_SWITCH_INSTRUCTION(decomp),
                                                       case_dinstr);

        }

    }

    g_hash_table_unref(sub_shared);
#endif
}