/* Chrysalide - Outil d'analyse de fichiers binaires
* cluster.c - mise en place de graphiques
*
* Copyright (C) 2016-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 Chrysalide. If not, see .
*/
#include "cluster.h"
#include
#include
#include
#include
#include "../gtkblockdisplay.h"
#include "../gtkbufferdisplay.h"
#include "../gtkdisplaypanel.h"
#include "../../common/sort.h"
#include "../../glibext/gloadedpanel.h"
/* Définition du tracé d'un lien */
typedef struct _incoming_edge
{
InstructionLinkType type; /* Complexité du tracé */
const size_t *hslot; /* Couche horizontale réservée */
GdkPoint y; /* Position des décrochages */
GdkPoint end; /* Point d'arrivée final */
GGraphEdge *edge; /* Lien complet en préparation */
GGraphCluster *origin; /* Bloc de départ du lien */
const size_t *leaving_index; /* Indice du point de départ */
} incoming_edge;
/* Détails sur le départ d'un lien */
typedef struct _leaving_edge
{
GdkPoint start; /* Point de départ d'un lien */
size_t index; /* Indice sur ligne de départ */
} leaving_edge;
/* Réservation pour les lignes horizontales */
typedef struct _hspace_booking
{
gint start; /* Abscisse de départ de ligne */
size_t index; /* Indice de rangée verticale */
} hspace_booking;
/* Découpage vertical */
typedef struct _graph_rank
{
size_t level; /* Niveau des blocs */
hspace_booking **right2left; /* Réservations de D -> G */
size_t r2l_count; /* Quantité de ces réservations*/
hspace_booking **left2right; /* Réservations de G -> D */
size_t l2r_count; /* Quantité de ces réservations*/
GGraphCluster **clusters; /* Ensembles de blocs */
size_t count; /* Nombre de ces ensembles */
} graph_rank;
/* Mise en disposition de blocs en graphique (instance) */
struct _GGraphCluster
{
GObject parent; /* A laisser en premier */
size_t *parent_index; /* Indice du lien au départ */
////////////////////////////////////////
gint top_margin[2]; /* Espaces gauches et droites */
gint left_margin; /* Espaces remontant à gauche */
gint right_margin; /* Espaces remontant à droite */
////////////////////////////////////////
incoming_edge **top_anchors; /* Accroches supérieures */
size_t ta_count; /* Quantité de ces accroches */
GCodeBlock *block; /* Bloc d'origine représenté */
GtkWidget *display; /* Vue graphique associée */
GtkAllocation alloc; /* Emplacement final du bloc */
leaving_edge **bottom_anchors; /* Accroches inférieures */
size_t ba_count; /* Quantité de ces accroches */
bool has_straight; /* Présence d'une ligne droite */
size_t straight_level; /* Rang atteint en ligne droite*/
size_t straight_index; /* Indice du lien vertical */
graph_rank *ranks; /* Répartition verticale */
size_t ranks_count; /* Nombre de divisions */
};
/* Mise en disposition de blocs en graphique (classe) */
struct _GGraphClusterClass
{
GObjectClass parent; /* A laisser en premier */
};
/* Espace minimal horizontal entre les blocs */
#define HORIZONTAL_MARGIN 20
/* Espace minimal vertical entre les blocs */
#define VERTICAL_MARGIN 15
/* Espace minimal entre les liens */
#define LINK_MARGIN 10
/* Initialise la classe des mises en disposition graphique. */
static void g_graph_cluster_class_init(GGraphClusterClass *);
/* Initialise une mise en disposition de blocs en graphique. */
static void g_graph_cluster_init(GGraphCluster *);
/* Supprime toutes les références externes. */
static void g_graph_cluster_dispose(GGraphCluster *);
/* Procède à la libération totale de la mémoire. */
static void g_graph_cluster_finalize(GGraphCluster *);
/* Complète un graphique avec un sous-ensemble de blocs. */
static void g_graph_cluster_add_sub(GGraphCluster *, GGraphCluster *, GCodeBlock *);
/* Organisation la disposition d'un ensemble de blocs basiques. */
static void g_graph_cluster_dispatch_x(GGraphCluster *);
/* Détermine une position pour un ensemble de blocs basiques. */
//static void g_graph_cluster_set_x(GGraphCluster *, gint);
/* Décalle vers la droite un ensemble de blocs basiques. */
static void g_graph_cluster_offset_x(GGraphCluster *, gint);
/* Décalle vers le bas un ensemble de blocs basiques. */
static void g_graph_cluster_set_y(GGraphCluster *, gint);
/* Recherche le bloc basique à l'extrémité d'un lien. */
//static GGraphCluster *g_graph_cluster_find_lower_dest_cluster(const GGraphCluster *, const GArchInstruction *);
/* Met en place les embryons de liens nécessaires. */
static void g_graph_cluster_define_links(GGraphCluster *, GHashTable *);
/* Calcule l'abscisse d'un lien à son départ d'un bloc. */
static gint g_graph_cluster_compute_leaving_link_position(const GGraphCluster *, size_t);
/* Détermine les abscisses de tous les liens en place. */
static void g_graph_cluster_compute_link_x_positions(GGraphCluster *);
/* Réserve de l'espace vertical pour les lignes horizontales. */
static void g_graph_cluster_book_hspace_for_links(GGraphCluster *);
/* Détermine les ordonnées de tous les liens en place. */
static void g_graph_cluster_compute_link_y_positions(GGraphCluster *);
/* Applique les positions calculées pour chaque lien graphique. */
static void g_graph_cluster_resolve_links(const GGraphCluster *);
/* Indique le type défini par la GLib pour les mises en disposition graphique. */
G_DEFINE_TYPE(GGraphCluster, g_graph_cluster, G_TYPE_OBJECT);
/******************************************************************************
* *
* Paramètres : klass = classe à initialiser. *
* *
* Description : Initialise la classe des mises en disposition graphique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_class_init(GGraphClusterClass *klass)
{
GObjectClass *object; /* Autre version de la classe */
object = G_OBJECT_CLASS(klass);
object->dispose = (GObjectFinalizeFunc/* ! */)g_graph_cluster_dispose;
object->finalize = (GObjectFinalizeFunc)g_graph_cluster_finalize;
}
/******************************************************************************
* *
* Paramètres : cluster = instance à initialiser. *
* *
* Description : Initialise une mise en disposition de blocs en graphique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_init(GGraphCluster *cluster)
{
}
/******************************************************************************
* *
* Paramètres : cluster = instance d'objet GLib à traiter. *
* *
* Description : Supprime toutes les références externes. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_dispose(GGraphCluster *cluster)
{
g_object_unref(G_OBJECT(cluster->block));
g_object_unref(G_OBJECT(cluster->display));
G_OBJECT_CLASS(g_graph_cluster_parent_class)->dispose(G_OBJECT(cluster));
}
/******************************************************************************
* *
* Paramètres : cluster = instance d'objet GLib à traiter. *
* *
* Description : Procède à la libération totale de la mémoire. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_finalize(GGraphCluster *cluster)
{
G_OBJECT_CLASS(g_graph_cluster_parent_class)->finalize(G_OBJECT(cluster));
}
/******************************************************************************
* *
* Paramètres : block = premier bloc du groupe. *
* highlighted = gestionnaire de surbrillance pour segments. *
* binary = binaire charger dont le code est à représenter.*
* *
* Description : *
* *
* Retour : Adresse de la structure mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GGraphCluster *g_graph_cluster_new(GCodeBlock *block, segcnt_list *highlighted, GLoadedBinary *binary)
{
GGraphCluster *result; /* Structure à retourner */
GBufferView *view; /* Partie affichée du tampon */
GtkRequisition requisition; /* Taille à l'écran actuelle */
result = g_object_new(G_TYPE_GRAPH_CLUSTER, NULL);
/* Encapsulation du bloc d'entrée */
result->block = block;
g_object_ref(G_OBJECT(block));
view = g_code_block_get_view(result->block, highlighted);
result->display = gtk_block_display_new(view);
gtk_widget_show(result->display);
g_loaded_panel_set_content(G_LOADED_PANEL(result->display), G_LOADED_CONTENT(binary));
gtk_block_display_override_view_index(GTK_BLOCK_DISPLAY(result->display), BVW_GRAPH);
gtk_display_panel_show_border(GTK_DISPLAY_PANEL(result->display), true);
/* Détermination d'une position initiale centrée */
gtk_widget_get_preferred_size(result->display, NULL, &requisition);
result->alloc.x = -requisition.width / 2;
result->alloc.y = 0;
result->alloc.width = requisition.width;
result->alloc.height = requisition.height;
printf("BLOCK INIT :: %d <> %d\n", result->alloc.x, result->alloc.width);
return result;
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à compléter. *
* sub = sous-ensemble à intégrer. *
* block = bloc basique de départ du sous-ensemble. *
* *
* Description : Complète un graphique avec un sous-ensemble de blocs. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_add_sub(GGraphCluster *cluster, GGraphCluster *sub, GCodeBlock *block)
{
size_t level; /* Niveau du nouvel ensemble */
size_t i; /* Boucle de parcours */
graph_rank new; /* Nouvel étage à insérer */
graph_rank *rank; /* Nouvel étage à compléter */
level = g_code_block_get_rank(block);
for (i = 0; i < cluster->ranks_count; i++)
if (cluster->ranks[i].level == level)
break;
if (i == cluster->ranks_count)
{
new.level = level;
new.right2left = NULL;
new.r2l_count = 0;
new.left2right = NULL;
new.l2r_count = 0;
new.clusters = (GGraphCluster **)calloc(1, sizeof(GGraphCluster *));
new.count = 1;
new.clusters[0] = sub;
int cmp_graph_rank(const graph_rank *a, const graph_rank *b)
{
if (a->level < b->level)
return -1;
else if (a->level > b->level)
return 1;
else
return 0;
}
cluster->ranks = qinsert(cluster->ranks, &cluster->ranks_count,
sizeof(graph_rank), (__compar_fn_t)cmp_graph_rank, &new);
}
else
{
rank = &cluster->ranks[i];
rank->count++;
rank->clusters = (GGraphCluster **)realloc(rank->clusters,
sizeof(GGraphCluster *) * rank->count);
rank->clusters[rank->count - 1] = sub;
}
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à manipuler. *
* *
* Description : Organisation la disposition d'un ensemble de blocs basiques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_dispatch_x(GGraphCluster *cluster)
{
size_t k; /* Boucle de parcours #1 */
const graph_rank *rank; /* Accès confortable au rang */
size_t j; /* Boucle de parcours #2 */
gint start; /* Position initiale de départ */
size_t rcount; /* Nombre d'ensembles présents */
size_t mid; /* Position centrale de départ */
/**
* A ce point, tous les blocs parents sont placés.
* On est donc en mesure de réorganiser les points d'arrivée
* des liens afin d'éviter les croisements : un lien qui vient
* de la gauche ne doit pas arriver tout à droite !
*/
int compare_incoming_edges(const incoming_edge **a, const incoming_edge **b)
{
int result; /* Bilan à retourner */
gint pos_a; /* Point de départ pour A */
gint pos_b; /* Point de départ pour B */
pos_a = g_graph_cluster_compute_leaving_link_position((*a)->origin, *(*a)->leaving_index);
pos_b = g_graph_cluster_compute_leaving_link_position((*b)->origin, *(*b)->leaving_index);
if (pos_a < pos_b)
result = -1;
else if (pos_a > pos_b)
result = 1;
else
result = 0;
return result;
}
qsort(cluster->top_anchors, cluster->ta_count, sizeof(incoming_edge *), (__compar_fn_t)compare_incoming_edges);
/**
* Il est désormais temps de placer tous les blocs de code inférieurs.
*/
void place_sub(GGraphCluster **iter, size_t loop, gint base, int dir)
{
size_t i; /* Boucle de parcours #2 */
GtkAllocation needed; /* Taille requise */
assert(dir == -1 || dir == 1);
for (i = 0; i < loop; i++, iter += dir)
{
g_graph_cluster_dispatch_x(*iter);
g_graph_cluster_compute_needed_alloc(*iter, &needed);
if (dir > 0)
g_graph_cluster_offset_x(*iter, base - needed.x);
else
g_graph_cluster_offset_x(*iter, base - needed.x - needed.width);
base += dir * (needed.width + HORIZONTAL_MARGIN);
}
}
for (k = 0; k < cluster->ranks_count; k++)
{
rank = &cluster->ranks[k];
/* Répartition autour d'une ligne verticale */
if (cluster->has_straight)
{
start = g_graph_cluster_compute_leaving_link_position(cluster, cluster->straight_index);
if (rank->level < cluster->straight_level)
{
/* Répartition à gauche du lien */
for (j = rank->count; j > 0; j--)
if (*rank->clusters[j - 1]->parent_index < cluster->straight_index)
break;
start -= HORIZONTAL_MARGIN / 2;
place_sub(rank->clusters, j, start, -1);
/* Répartition à droite du lien */
for (j = 0; j < rank->count; j++)
if (*rank->clusters[j]->parent_index > cluster->straight_index)
break;
start += HORIZONTAL_MARGIN;
place_sub(rank->clusters + j, rank->count - j, start, 1);
}
}
/* Répartition homogène */
else
{
rcount = rank->count;
if (rcount % 2 == 1)
{
if (rcount > 1)
{
mid = rcount / 2;
start = rank->clusters[mid]->alloc.x - HORIZONTAL_MARGIN;
place_sub(rank->clusters + mid - 1, mid, start, -1);
start *= -1;
place_sub(rank->clusters + mid + 1, mid, start, 1);
}
else
g_graph_cluster_dispatch_x(rank->clusters[0]);
}
else
{
mid = rcount / 2 - 1;
start = - HORIZONTAL_MARGIN / 2;
place_sub(rank->clusters + mid, mid + 1, start, -1);
start *= -1;
place_sub(rank->clusters + mid + 1, mid + 1, start, 1);
}
}
}
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à actualiser. *
* x = position finale sur l'axe des abscisses. *
* *
* Description : Détermine une position pour un ensemble de blocs basiques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
/*
static void g_graph_cluster_set_x(GGraphCluster *cluster, gint x)
{
g_graph_cluster_offset_x(cluster, -cluster->alloc.x);
g_graph_cluster_offset_x(cluster, x);
}
*/
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à actualiser. *
* offset = décalage à appliquer. *
* *
* Description : Décalle vers la droite un ensemble de blocs basiques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_offset_x(GGraphCluster *cluster, gint offset)
{
size_t i; /* Boucle de parcours #1 */
size_t j; /* Boucle de parcours #2 */
cluster->alloc.x += offset;
for (i = 0; i < cluster->ranks_count; i++)
for (j = 0; j < cluster->ranks[i].count; j++)
g_graph_cluster_offset_x(cluster->ranks[i].clusters[j], offset);
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à actualiser. *
* base = position ordonnée à appliquer. *
* *
* Description : Décalle vers le bas un ensemble de blocs basiques. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_set_y(GGraphCluster *cluster, gint base)
{
size_t i; /* Boucle de parcours #1 */
const graph_rank *rank; /* Accès simplifié à la rangée */
gint max; /* Hauteur maximale rencontrée */
size_t j; /* Boucle de parcours #2 */
GGraphCluster *sub; /* Sous-ensemble traité */
GtkAllocation alloc; /* Allocation courante */
cluster->alloc.y = base;
base += cluster->alloc.height;
for (i = 0; i < cluster->ranks_count; i++)
{
rank = &cluster->ranks[i];
/* On ajoute l'espace vertical pour les lignes horizontales */
if (rank->r2l_count > rank->l2r_count)
max = rank->r2l_count;
else
max = rank->l2r_count;
base += VERTICAL_MARGIN;
/**
* Comme les liens purement verticaux n'entrainent pas de réservation,
* il n'y a potentiellement pas toujours d'espace à ajouter.
*/
if (max > 0)
base += ((max - 1) * LINK_MARGIN);
/* On ajoute l'espace requis pour l'affichage des blocs */
base += VERTICAL_MARGIN;
max = 0;
for (j = 0; j < rank->count; j++)
{
sub = rank->clusters[j];
g_graph_cluster_set_y(sub, base);
g_graph_cluster_compute_needed_alloc(sub, &alloc);
if ((alloc.y + alloc.height) > max)
max = alloc.y + alloc.height;
}
base = max;
}
}
/******************************************************************************
* *
* Paramètres : cluster = encapsulation à consulter. *
* alloc = emplacement idéal pour l'affichage. *
* *
* Description : Détermine l'emplacement requis d'un ensemble de blocs. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_cluster_compute_needed_alloc(const GGraphCluster *cluster, GtkAllocation *alloc)
{
GtkAllocation needed; /* Taille requise */
size_t i; /* Boucle de parcours */
size_t rcount; /* Nombre d'ensembles présents */
*alloc = cluster->alloc;
for (i = 0; i < cluster->ranks_count; i++)
{
rcount = cluster->ranks[i].count;
switch (rcount)
{
case 1:
g_graph_cluster_compute_needed_alloc(cluster->ranks[i].clusters[0], &needed);
if (needed.x < alloc->x)
{
alloc->width += (alloc->x - needed.x);
alloc->x = needed.x;
}
if ((needed.x + needed.width) > (alloc->x + alloc->width))
alloc->width += needed.x + needed.width - alloc->x - alloc->width;
/* La hauteur maximale n'est présente qu'avec le dernier morceau */
if ((i + 1) == cluster->ranks_count)
{
if ((needed.y + needed.height) > (alloc->y + alloc->height))
alloc->height += needed.y + needed.height - alloc->y - alloc->height;
}
break;
default:
assert(rcount >= 2);
g_graph_cluster_compute_needed_alloc(cluster->ranks[i].clusters[0], &needed);
if (needed.x < alloc->x)
{
alloc->width += (alloc->x - needed.x);
alloc->x = needed.x;
}
/* La hauteur maximale n'est présente qu'avec le dernier morceau */
if ((i + 1) == cluster->ranks_count)
{
if ((needed.y + needed.height) > (alloc->y + alloc->height))
alloc->height += needed.y + needed.height - alloc->y - alloc->height;
}
g_graph_cluster_compute_needed_alloc(cluster->ranks[i].clusters[rcount - 1], &needed);
if ((needed.x + needed.width) > (alloc->x + alloc->width))
alloc->width += needed.x + needed.width - alloc->x - alloc->width;
/* La hauteur maximale n'est présente qu'avec le dernier morceau */
if ((i + 1) == cluster->ranks_count)
{
if ((needed.y + needed.height) > (alloc->y + alloc->height))
alloc->height += needed.y + needed.height - alloc->y - alloc->height;
}
break;
}
}
}
/******************************************************************************
* *
* Paramètres : cluster = encapsulation à consulter. *
* *
* Description : Fournit le composant graphique principal du groupe. *
* *
* Retour : Composant graphique principal utilisé. *
* *
* Remarques : - *
* *
******************************************************************************/
GtkWidget *g_graph_cluster_get_widget(GGraphCluster *cluster)
{
GtkWidget *result;
result = cluster->display;
g_object_ref(G_OBJECT(result));
return result;
}
/******************************************************************************
* *
* Paramètres : cluster = encapsulation à traiter. *
* display = support de destination finale. *
* *
* Description : Dispose chaque noeud sur la surface de destination donnée. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
void g_graph_cluster_place(GGraphCluster *cluster, GtkGraphDisplay *display)
{
size_t i; /* Boucle de parcours #1 */
size_t j; /* Boucle de parcours #2 */
g_object_ref(G_OBJECT(cluster->display));
gtk_graph_display_put(display, cluster->display, &cluster->alloc);
for (i = 0; i < cluster->ta_count; i++)
{
g_object_ref(G_OBJECT(cluster->top_anchors[i]->edge));
gtk_graph_display_add_edge(display, cluster->top_anchors[i]->edge);
}
for (i = 0; i < cluster->ranks_count; i++)
for (j = 0; j < cluster->ranks[i].count; j++)
g_graph_cluster_place(cluster->ranks[i].clusters[j], display);
}
//////////////////////////////////////////////////////////
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à consulter ou remonter. *
* first = première instruction du bloc basique recherché. *
* *
* Description : Recherche le bloc basique à l'extrémité d'un lien. *
* *
* Retour : Bloc graphique de destination pour un lien ou NULL si échec. *
* *
* Remarques : - *
* *
******************************************************************************/
#if 0
static GGraphCluster *g_graph_cluster_find_lower_dest_cluster(const GGraphCluster *cluster, const GArchInstruction *first)
{
GGraphCluster *result; /* Trouvaille à retourner */
size_t i; /* Boucle de parcours #1 */
const graph_rank *rank; /* Confort d'accès */
size_t j; /* Boucle de parcours #2 */
GArchInstruction *instr; /* Instruction à comparer */
result = NULL;
for (i = 0; i < cluster->ranks_count && result == NULL; i++)
{
rank = &cluster->ranks[i];
for (j = 0; j < rank->count && result == NULL; j++)
{
g_basic_block_get_boundaries(rank->clusters[j]->block, &instr, NULL);
if (instr == first)
result = rank->clusters[j];
}
}
return result;
}
#endif
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à actualiser. *
* all = table regroupant tous les groupes créés. *
* *
* Description : Met en place les embryons de liens nécessaires. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_define_links(GGraphCluster *cluster, GHashTable *all)
{
size_t level; /* Niveau du nouvel ensemble */
size_t dcount; /* Nombre de liens de dest. */
size_t i; /* Boucle de parcours #1 */
const block_link_t *dest; /* Bloc visé par une autre */
GGraphCluster *target; /* Bloc ciblé par un lien */
leaving_edge *leaving; /* Point de départ d'un lien */
size_t target_level; /* Rang du bloc ciblé */
size_t j; /* Boucle de parcours #2 */
size_t k; /* Boucle de parcours #3 */
incoming_edge *incoming; /* Définitions d'arrivée */
/* Au niveau du bloc courant */
level = g_code_block_get_rank(cluster->block);
g_code_block_lock_dest(cluster->block);
dcount = g_code_block_count_destinations(cluster->block);
for (i = 0; i < dcount; i++)
{
dest = g_code_block_get_destination(cluster->block, i);
switch (dest->type)
{
case ILT_EXEC_FLOW:
case ILT_JUMP:
case ILT_CASE_JUMP:
case ILT_JUMP_IF_TRUE:
case ILT_JUMP_IF_FALSE:
target = G_GRAPH_CLUSTER(g_hash_table_lookup(all, dest->linked));
assert(target != NULL);
/* Point de départ */
leaving = (leaving_edge *)calloc(1, sizeof(leaving_edge));
cluster->bottom_anchors = (leaving_edge **)realloc(cluster->bottom_anchors,
++cluster->ba_count * sizeof(leaving_edge *));
cluster->bottom_anchors[cluster->ba_count - 1] = leaving;
/* Est-ce un lien qui doit être vertical ? */
/* FIXME : trop de boucles ! */
target_level = -1;
for (j = 0; j < cluster->ranks_count && target_level == -1; j++)
for (k = 0; k < cluster->ranks[j].count && target_level == -1; k++)
if (cluster->ranks[j].clusters[k] == target)
{
target_level = cluster->ranks[j].level;
if (target_level > (level + 1) && target_level > cluster->straight_level)
{
cluster->has_straight = true;
cluster->straight_level = target_level;
cluster->straight_index = cluster->ba_count - 1;
}
}
/* Point d'arrivée */
incoming = (incoming_edge *)calloc(1, sizeof(incoming_edge));
target->top_anchors = (incoming_edge **)realloc(target->top_anchors,
++target->ta_count * sizeof(incoming_edge *));
target->top_anchors[target->ta_count - 1] = incoming;
/* Etablissement d'un embryon de lien */
leaving->index = cluster->ba_count - 1;
incoming->type = dest->type;
if (incoming->type == ILT_JUMP_IF_TRUE)
incoming->edge = g_graph_edge_new_true(&leaving->start, &incoming->y, &incoming->end);
else if (incoming->type == ILT_JUMP_IF_FALSE)
incoming->edge = g_graph_edge_new_false(&leaving->start, &incoming->y, &incoming->end);
else
incoming->edge = g_graph_edge_new(&leaving->start, &incoming->y, &incoming->end);
incoming->origin = cluster;
incoming->leaving_index = &leaving->index;
/* FIXME : trop de boucles ! */
target_level = -1;
for (j = 0; j < cluster->ranks_count && target_level == -1; j++)
for (k = 0; k < cluster->ranks[j].count && target_level == -1; k++)
if (cluster->ranks[j].clusters[k] == target)
{
target_level = 0;
target->parent_index = &leaving->index;
}
break;
case ILT_LOOP:
/* TODO */
//assert(false);
break;
default:
break;
}
unref_block_link(dest);
}
g_code_block_unlock_dest(cluster->block);
/* Déplacement d'un éventuel lien central */
if (cluster->has_straight)
{
size_t center;
leaving_edge *tmp;
if (cluster->ba_count % 2 == 0)
center = cluster->ba_count / 2 - 1;
else
center = cluster->ba_count / 2;
if (cluster->straight_index < center)
{
tmp = cluster->bottom_anchors[cluster->straight_index];
memmove(cluster->bottom_anchors + cluster->straight_index,
cluster->bottom_anchors + cluster->straight_index + 1,
(center - cluster->straight_index) * sizeof(leaving_edge *));
cluster->bottom_anchors[center] = tmp;
for (i = cluster->straight_index; i <= center; i++)
cluster->bottom_anchors[i]->index = i;
cluster->straight_index = center;
}
else if (cluster->straight_index > center)
{
tmp = cluster->bottom_anchors[cluster->straight_index];
memmove(cluster->bottom_anchors + center + 1,
cluster->bottom_anchors + center,
(cluster->straight_index - center) * sizeof(leaving_edge *));
cluster->bottom_anchors[center] = tmp;
for (i = center; i <= cluster->straight_index ; i++)
cluster->bottom_anchors[i]->index = i;
cluster->straight_index = center;
}
}
/* Propagation de la mise en place */
for (i = 0; i < cluster->ranks_count; i++)
for (j = 0; j < cluster->ranks[i].count; j++)
g_graph_cluster_define_links(cluster->ranks[i].clusters[j], all);
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à consulter. *
* index = indice du lien à considérer. *
* *
* Description : Calcule l'abscisse d'un lien à son départ d'un bloc. *
* *
* Retour : Abscisse à attribuer à un départ de lien. *
* *
* Remarques : - *
* *
******************************************************************************/
static gint g_graph_cluster_compute_leaving_link_position(const GGraphCluster *cluster, size_t index)
{
gint result; /* Position à retourner */
gint mid_x; /* Abscisse centrale */
size_t half; /* Indice de répartition égale */
assert(index < cluster->ba_count);
mid_x = cluster->alloc.x + (cluster->alloc.width / 2);
half = cluster->ba_count / 2;
if (cluster->ba_count % 2 == 1)
{
if (index < half)
result = mid_x - (half - index) * LINK_MARGIN;
else if (index == half)
result = mid_x;
else
result = mid_x + (index - half) * LINK_MARGIN;
}
else
{
if (index < half)
result = mid_x - LINK_MARGIN / 2 - (half - index - 1) * LINK_MARGIN;
else
result = mid_x + LINK_MARGIN / 2 + (index - half) * LINK_MARGIN;
}
return result;
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à actualiser. *
* *
* Description : Détermine les abscisses de tous les liens en place. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_compute_link_x_positions(GGraphCluster *cluster)
{
gint mid_x; /* Abscisse centrale */
size_t i; /* Boucle de parcours #1 */
size_t half; /* Indice de répartition égale */
GdkPoint *pt; /* Point à actualiser */
size_t j; /* Boucle de parcours #2 */
mid_x = cluster->alloc.x + (cluster->alloc.width / 2);
/* Du côté des départs... */
if (cluster->ba_count > 0)
for (i = 0; i < cluster->ba_count; i++)
{
pt = &cluster->bottom_anchors[i]->start;
pt->x = g_graph_cluster_compute_leaving_link_position(cluster, i);
}
/* Du côté des arrivées... */
if (cluster->ta_count > 0)
{
half = cluster->ta_count / 2;
if (cluster->ta_count % 2 == 1)
{
for (i = half; i > 0; i--)
{
pt = &cluster->top_anchors[i - 1]->end;
pt->x = mid_x - (half - i + 1) * LINK_MARGIN;
}
cluster->top_anchors[half]->end.x = mid_x;
for (i = half + 1; i < cluster->ta_count; i++)
{
pt = &cluster->top_anchors[i]->end;
pt->x = mid_x + (i - half) * LINK_MARGIN;
}
}
else
{
for (i = half; i > 0; i--)
{
pt = &cluster->top_anchors[i - 1]->end;
pt->x = mid_x - LINK_MARGIN / 2 - (half - i) * LINK_MARGIN;
}
for (i = half; i < cluster->ta_count; i++)
{
pt = &cluster->top_anchors[i]->end;
pt->x = mid_x + LINK_MARGIN / 2 + (half - i) * LINK_MARGIN;
}
}
}
/* Propagation des déterminations */
for (i = 0; i < cluster->ranks_count; i++)
for (j = 0; j < cluster->ranks[i].count; j++)
g_graph_cluster_compute_link_x_positions(cluster->ranks[i].clusters[j]);
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à traiter. *
* *
* Description : Réserve de l'espace vertical pour les lignes horizontales. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_book_hspace_for_links(GGraphCluster *cluster)
{
size_t i; /* Boucle de parcours #1 */
graph_rank *rank; /* Rangée à manipuler */
size_t j; /* Boucle de parcours #2 */
GGraphCluster *sub; /* Bloc inférieur à manipuler */
size_t k; /* Boucle de parcours #3 */
gint x1; /* Abscisse de départ de lien */
gint x2; /* Abscisse d'arrivée de lien */
hspace_booking *new; /* Nouvelle réservation */
for (i = 0; i < cluster->ranks_count; i++)
{
rank = &cluster->ranks[i];
/* Enregistrement des besoins */
for (j = 0; j < rank->count; j++)
{
sub = rank->clusters[j];
for (k = 0; k < sub->ta_count; k++)
{
g_graph_edge_get_x_borders(sub->top_anchors[k]->edge, &x1, &x2);
if (x1 > x2)
{
new = (hspace_booking *)calloc(1, sizeof(hspace_booking));
new->start = x1;
sub->top_anchors[k]->hslot = &new->index;
int compare_right_to_left(const hspace_booking **a, const hspace_booking **b)
{
int result; /* Bilan à retourner */
if ((*a)->start > (*b)->start)
result = -1;
else if ((*a)->start < (*b)->start)
result = 1;
else
{
assert(false);
result = 0;
}
return result;
}
rank->right2left = qinsert(rank->right2left, &rank->r2l_count,
sizeof(hspace_booking *),
(__compar_fn_t)compare_right_to_left, &new);
}
else if (x1 < x2)
{
new = (hspace_booking *)calloc(1, sizeof(hspace_booking));
new->start = x1;
sub->top_anchors[k]->hslot = &new->index;
int compare_left_to_right(const hspace_booking **a, const hspace_booking **b)
{
int result; /* Bilan à retourner */
if ((*a)->start < (*b)->start)
result = -1;
else if ((*a)->start > (*b)->start)
result = 1;
else
{
assert(false);
result = 0;
}
return result;
}
rank->left2right = qinsert(rank->left2right, &rank->l2r_count,
sizeof(hspace_booking *),
(__compar_fn_t)compare_left_to_right, &new);
}
else
sub->top_anchors[k]->hslot = NULL;
}
}
/* Définition des couches */
for (j = 0; j < rank->r2l_count; j++)
rank->right2left[j]->index = j;
for (j = 0; j < rank->l2r_count; j++)
rank->left2right[j]->index = j;
/* Propagation des déterminations */
for (j = 0; j < rank->count; j++)
g_graph_cluster_book_hspace_for_links(rank->clusters[j]);
}
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à actualiser. *
* *
* Description : Détermine les ordonnées de tous les liens en place. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_compute_link_y_positions(GGraphCluster *cluster)
{
gint y; /* Ordonnée d'application */
size_t i; /* Boucle de parcours #1 */
incoming_edge *incoming; /* Raccourci pour le confort */
size_t j; /* Boucle de parcours #2 */
/* Du côté des départs... */
if (cluster->ba_count > 0)
{
y = cluster->alloc.y + cluster->alloc.height;
for (i = 0; i < cluster->ba_count; i++)
cluster->bottom_anchors[i]->start.y = y;
}
/* Du côté des arrivées... */
if (cluster->ta_count > 0)
{
y = cluster->alloc.y;
for (i = 0; i < cluster->ta_count; i++)
{
incoming = cluster->top_anchors[i];
incoming->end.y = y;
incoming->y.y = incoming->end.y - VERTICAL_MARGIN;
if (incoming->hslot != NULL)
incoming->y.y -= *incoming->hslot * LINK_MARGIN;
}
}
/* Propagation des déterminations */
for (i = 0; i < cluster->ranks_count; i++)
for (j = 0; j < cluster->ranks[i].count; j++)
g_graph_cluster_compute_link_y_positions(cluster->ranks[i].clusters[j]);
}
/******************************************************************************
* *
* Paramètres : cluster = graphique de blocs à consulter. *
* *
* Description : Applique les positions calculées pour chaque lien graphique. *
* *
* Retour : - *
* *
* Remarques : - *
* *
******************************************************************************/
static void g_graph_cluster_resolve_links(const GGraphCluster *cluster)
{
size_t i; /* Boucle de parcours #1 */
size_t j; /* Boucle de parcours #2 */
for (i = 0; i < cluster->ta_count; i++)
g_graph_edge_resolve(cluster->top_anchors[i]->edge);
for (i = 0; i < cluster->ranks_count; i++)
for (j = 0; j < cluster->ranks[i].count; j++)
g_graph_cluster_resolve_links(cluster->ranks[i].clusters[j]);
}
/* Liste de blocs restants à traiter */
typedef struct _pending_blocks
{
size_t count; /* Taille de la liste */
GCodeBlock *list[0]; /* Liste de blocs à traiter */
} pending_blocks;
/* Met en place un ensemble de blocs sous forme graphique. */
static GGraphCluster *setup_graph_clusters(GLoadedBinary *, const GBlockList *, size_t , segcnt_list *, pending_blocks *, GHashTable *);
/******************************************************************************
* *
* Paramètres : binary = binaire charger dont le code est à représenter.*
* list = ensemble de blocs basiques à manipuler. *
* index = indice du bloc principal à mettre en place. *
* highlighted = gestionnaire de surbrillance pour segments. *
* pending = liste de blocs restant à traiter. [OUT] *
* all = table regroupant tous les groupes créés. *
* *
* Description : Met en place un ensemble de blocs sous forme graphique. *
* *
* Retour : Ensemble de blocs mis en place. *
* *
* Remarques : - *
* *
******************************************************************************/
static GGraphCluster *setup_graph_clusters(GLoadedBinary *binary, const GBlockList *list, size_t index, segcnt_list *highlighted, pending_blocks *pending, GHashTable *all)
{
GGraphCluster *result; /* Instance nouvelle à renvoyer*/
GCodeBlock *block; /* Bloc à manipuler */
#ifndef NDEBUG
gboolean new; /* Bloc déjà traité ? */
#endif
size_t dcount; /* Nombre de liens de dest. */
size_t i; /* Boucle de parcours #1 */
const block_link_t *dest; /* Bloc visé par une autre */
size_t j; /* Boucle de parcours #2 */
bool changed; /* Un ajout a été effectué ? */
const bitfield_t *dominators; /* Blocs dominant ce même bloc */
size_t next; /* Indice du prochain bloc */
GGraphCluster *sub; /* Sous-ensemble à intégrer */
block = g_block_list_get_block(list, index);
result = g_graph_cluster_new(block, highlighted, binary);
#ifndef NDEBUG
new = g_hash_table_insert(all, block, result);
assert(new);
#else
g_hash_table_insert(all, block, result);
#endif
/* Détermination des blocs suivants */
g_code_block_lock_dest(block);
dcount = g_code_block_count_destinations(block);
for (i = 0; i < dcount; i++)
{
dest = g_code_block_get_destination(block, i);
switch (dest->type)
{
case ILT_EXEC_FLOW:
case ILT_JUMP:
case ILT_CASE_JUMP:
case ILT_JUMP_IF_TRUE:
case ILT_JUMP_IF_FALSE:
for (j = 0; j < pending->count; j++)
if (pending->list[j] == dest->linked)
break;
if (j == pending->count)
{
/**
* Il faut vérifier ici si la destination n'a pas déjà été
* empruntée, sauf peine de faire réagir l'assertion plus
* haut au moment de l'insertion.
*
* Le type de code à problème est le suivant :
*
* ...
* if (...)
* ...
* ...
*
* Le code suivant le bloc conditionnel a deux origines,
* qui vont chacune poursuivre le traitement vers ce code
* commun.
*
* Et comme les origines ne sont pas dominantes, on utilise
* la table globale.
*/
if (!g_hash_table_contains(all, dest->linked))
{
g_object_ref(G_OBJECT(dest->linked));
assert((pending->count + 1) < g_block_list_count_blocks(list));
pending->list[pending->count++] = dest->linked;
}
}
break;
default:
break;
}
unref_block_link(dest);
}
g_code_block_unlock_dest(block);
g_object_unref(G_OBJECT(block));
/* Intégration de tous les blocs en attente */
do
{
changed = false;
for (i = 0; i < pending->count && !changed; i++)
{
block = pending->list[i];
dominators = g_code_block_get_domination(block);
if (test_in_bit_field(dominators, index))
{
/* Dépilement */
changed = true;
if ((i + 1) < pending->count)
memmove(&pending->list[i], &pending->list[i + 1],
(pending->count - i - 1) * sizeof(GCodeBlock *));
pending->count--;
/* Intégration */
next = g_code_block_get_index(block);
assert(next < g_block_list_count_blocks(list));
sub = setup_graph_clusters(binary, list, next, highlighted, pending, all);
g_graph_cluster_add_sub(result, sub, block);
}
g_object_ref(G_OBJECT(block));
}
}
while (changed);
return result;
}
/******************************************************************************
* *
* Paramètres : blocXXXXXXXXXXXXXXXXXXXXks = ensemble des blocs basiques déjà découpés. *
* views = morceaux de codXXXXXXXXXXXXXXXXXxe à afficher de façon organisée. *
* count = quantité de ces morceaux de code.XXXXXXXXXXXXXXXXXX *
* *
* Description : Construit un graphique à partir de blocs basiques. *
* *
* Retour : Adresse de la structure mise en place. *
* *
* Remarques : - *
* *
******************************************************************************/
GGraphCluster *bootstrap_graph_cluster(GLoadedBinary *binary, const GBlockList *list, segcnt_list *highlighted)
{
GGraphCluster *result; /* Structure à retourner */
GHashTable *all; /* Collection des créations */
size_t count; /* Taille de la liste de blocs */
pending_blocks *pending; /* Suivi des blocs à traiter */
GtkAllocation needed; /* Taille requise */
all = g_hash_table_new(NULL, NULL);
count = g_block_list_count_blocks(list);
pending = (pending_blocks *)calloc(1, sizeof(pending_blocks) + count * sizeof(GCodeBlock *));
result = setup_graph_clusters(binary, list, 0, highlighted, pending, all);
free(pending);
g_graph_cluster_define_links(result, all);
//show_tree(result, 0);
//printf("--------------\n");
/* Positionnements dans l'espace */
g_graph_cluster_dispatch_x(result);
/* Réajustement vers la droite */
g_graph_cluster_compute_needed_alloc(result, &needed);
g_graph_cluster_offset_x(result, -needed.x);
/* Application finale sur les liens */
g_graph_cluster_compute_link_x_positions(result);
g_graph_cluster_book_hspace_for_links(result);
g_graph_cluster_set_y(result, 0);
g_graph_cluster_compute_link_y_positions(result);
g_graph_cluster_resolve_links(result);
g_hash_table_unref(all);
return result;
}