Précédent : Logiciels Remonter : Projet LANDE, Conception et
validation Suivant : Actions industrielles
Nous présentons d'abord des résultats généraux sur l'analyse
de programmes (modules et
) avant de
détailler un certain nombre de recherches portant sur des
analyses particulières: la vérification de propriétés de sécurité
(module
) et la compilation de
Prolog (module
). Nous
décrivons ensuite nos travaux récents sur le débogage statique
(module
), le débogage dynamique
(modules
et
) et la génération
de jeux de test (module
).
Participants : Valérie Gouranton , Daniel Le Métayer
Mots-clés : Analyse de programmes, Sémantique naturelle,
Slicing
Résumé : Certaines analyses, comme le slicing (cf. module) ne sont pas restreintes à un langage de programmation particulier. Plutôt que d'en fournir une nouvelle définition pour chaque langage, nous avons montré qu'il est possible de définir une telle analyse de manière générique et de l'instancier pour obtenir des analyses particulières. Nous avons proposé pour ce faire un format de sémantique naturelle et nous avons appliqué cette technique à l'analyse de slicing. Nous avons pu ainsi dériver, par simple instanciation d'une définition générique, des analyses dynamiques et statiques pour un langage impératif, un langage fonctionnel et un langage de programmation logique.
De nombreux travaux ont été effectués sur les fondements de l'analyse sémantique, la correction et la précision des analyseurs. On peut regretter cependant le peu d'attention accordé jusqu'à présent à la conception d'outils d'analyse génériques. Il est ainsi très difficile de factoriser les efforts en matière de réalisation d'analyseurs. Nous avons abordé ce problème en considérant la construction d'analyseurs sous l'angle de la dérivation de programmes à partir de spécifications [11,21]. Le programme en l'occurrence est l'analyseur lui-même et sa spécification est composée de deux parties:
L'intérêt de cette démarche est qu'elle permet d'exprimer dans
un même cadre des analyses variées sur différents langages. Nous
en avons fait la démonstration pour des analyses de programmes
impératifs (durée de vie des variables), logiques (clôture des
termes) et fonctionnels (nécessité, globalisation) [11]. Certaines de ces analyses sont
spécifiques à un langage de programmation: on peut citer dans
cette catégorie l'analyse de clôture pour la programmation
logique. D'autres, comme le slicing (cf. module ) ont un intérêt plus général.
Plutôt que d'en fournir une nouvelle définition pour chaque
langage, nous avons montré qu'il est possible de définir une
telle analyse de manière générique et de l'instancier pour
obtenir des analyses particulières. Nous avons proposé pour ce
faire un format de sémantique naturelle et nous avons
appliqué cette technique à l'analyse de slicing. Nous
avons pu ainsi dériver, par simple instanciation d'une définition
générique, des analyses dynamiques et statiques pour un langage
impératif, un langage fonctionnel et un langage de programmation
logique [11]. Nous nous
intéressons maintenant à l'application de cette méthode à
d'autres analyses d'intérêt général comme l'analyse de partage et
à sa mise en oeuvre dans un outil d'aide à la conception
d'analyseurs.
Participant : Thomas Jensen
Mots-clés : Typage non-standard, Logique de programmes,
Modularité
Résumé : Nous avons proposé une méthode de spécification d'analyses, où une analyse est définie comme une logique de programmes par des règles d'inférence. Le cadre peut accommoder plusieurs analyses d'une précision variable en ajoutant des connecteurs logiques (conjonction, disjonction, etc.). En nous appuyant sur des résultats récents concernant l'inférence de types d'intersection et de types polymorphes, nous avons conçu un algorithme qui permet de déduire pour chaque programme une propriété principale à partir de laquelle toute autre propriété démontrable par l'analyse peut être déterminée. Un avantage de cet algorithme est qu'il traite des fragments de programmes aussi bien que des programmes entiers. Il s'agit donc d'une démarche qui, à plus long terme, peut mener à un cadre général pour l'analyse modulaire.
Nous avons proposé une méthode de spécification d'analyses, où une analyse est définie comme une logique de programmes par des règles d'inférence. Le cadre peut accommoder plusieurs analyses d'une précision variable en ajoutant des connecteurs logiques (conjonction, disjonction, etc.). Nous avons focalisé nos recherches sur une logique avec propriétés disjonctives pour un langage avec procédures d'ordre supérieur et types récursifs (listes, arbres, etc.). Cette activité a abouti à un cadre qui réunit et étend plusieurs techniques d'analyse existantes [16].
Exprimer une analyse par des règles d'inférence comme un système de typage non-standard présente l'avantage de la rendre plus compréhensible mais permet également de s'appuyer sur des algorithmes de typage connus. Dans des travaux antérieurs, nous avons conçu une méthode efficace pour vérifier qu'un programme satisfait une propriété donnée. Se pose alors la question de savoir s'il est possible, étant donné seulement le programme, de trouver les propriétés satisfaites par ce programme, c'est-à-dire inférer les propriétés. En nous appuyant sur des résultats récents concernant l'inférence de types d'intersection et de types polymorphes, nous avons conçu un algorithme qui permet de déduire pour chaque programme une propriété principale à partir de laquelle toute autre propriété démontrable par l'analyse peut être déterminée. Un avantage de cet algorithme est qu'il traite des fragments de programmes aussi bien que des programmes entiers. Il s'agit donc d'une démarche qui, à plus long terme, peut mener à un cadre général pour l'analyse modulaire [24].
Participants : Thomas Jensen , Daniel Le Métayer , Tommy
Thorn
Mots-clés : Téléchargement, Sécurité, Chargement
dynamique, Java
Résumé : Nous nous sommes attaqués à la formalisation de la sémantique du langage Java en nous focalisant sur les règles de visibilité (des classes et de leurs membres) et leur évolution lors du chargement dynamique de classes. Il s'agit en effet de caractéristiques particulières de Java qui ont un impact direct sur la sécurité et dont les définitions informelles ne sont pas exemptes d'ambiguïtés ou d'insuffisances. Cette formalisation en terme de systèmes d'inférence nous a permis de décrire de manière rigoureuse l'origine d'une erreur de sécurité qui avait été découverte empiriquement par des chercheurs d'ATT.
L'un des intérêts majeurs du langage Java est la possibilité de télécharger du code et de l'exécuter de manière transparente. Cette pratique soulève de sérieux problèmes en matière de sécurité des informations (confidentialité, intégrité notamment) et Java y répond, entre autres, par une plus grande sûreté de programmation (typage, vérification de code importé, héritage simple, etc.), un gestionnaire de sécurité et des recommandations en matière de politique de sécurité [18]. Le langage lui-même est supposé sûr mais cette affirmation est toujours sujette à débats. Il ressort clairement des déclarations contradictoires à ce sujet que Java intègre un certain nombre de dispositions complexes qui rendent indispensable la formalisation du langage (pour le moins de certains de ses aspects critiques). Nous nous sommes attaqués à cette formalisation en nous focalisant sur les règles de visibilité (des classes et de leurs membres) et leur évolution lors du chargement dynamique de classes [34]. Il s'agit en effet de caractéristiques particulières de Java qui ont un impact direct sur la sécurité et dont les définitions informelles ne sont pas exemptes d'ambiguïtés ou d'insuffisances. Cette formalisation en terme de systèmes d'inférence nous a permis de décrire de manière rigoureuse l'origine d'une erreur de sécurité qui avait été découverte empiriquement par des chercheurs d'ATT. Notre objectif actuel est de définir de la même manière des politiques de sécurité afin de pouvoir étudier leur effet (c'est à dire les exigences qu'elles imposent) sur les composants d'une application Java (comme le chargeur de classes).
Participant : Olivier Ridoux
Mots-clés : Analyse statique, Prolog
Résumé : Nous étudions l'analyse statique deProlog par la technique de la compilation abstraite où un programme source est traduit en un autre programme dont les résultats sont interprétés comme le résultat de l'analyse du programme source. L'extension à
Prolog des techniques éprouvées dans le cas de Prolog nécessite entre autres le traitement des
-termes simplement typés.
Nous avons choisi pour l'analyse statique de Prolog la technique de la compilation
abstraite où un programme source est traduit en un autre
programme dont les résultats sont interprétés comme le résultat
de l'analyse du programme source. Cette méthode a déjà été
employée pour Prolog et l'appliquer à
Prolog
nécessite des extensions dans trois directions:
Le domaine de calcul choisi pour les programmes abstraits est celui des booléens. Dans ce cadre, nous étudions actuellement une solution au deuxième problème qui consiste à coder par un vecteur booléen une fonction de transfert de la propriété à analyser et à calculer pour chaque terme d'ordre supérieur sa contribution à la propriété et sa fonction de transfert. Ce travail fait l'objet d'une thèse à l'École des Mines de Nantes (Frédéric Malésieux) co-encadrée par Olivier Ridoux et Patrice Boizumault.
Participants : Pascal Fradet , Ronan Gaugne , Daniel Le
Métayer , Florimond Ployette
Mots-clés : Pointeur, Déréférence, Structure de données,
Mise au point, Débogage statique
Résumé : Nous avons proposé une analyse statique de programmes pour la détection d'accès incorrects à la mémoire (déréférences de pointeurs invalides). Cet analyseur est très précis: il est capable par exemple de décrire exactement le partage induit par des structures complexes comme les listes circulaires. Dédié à l'aide à la mise au point de programmes, il permet un certain nombre d'interactions avec l'utilisateur. Celles-ci se font à l'aide d'un langage d'annotations qui s'intègre naturellement au formalisme de l'analyseur tout en restant très proche du langage de programmation afin de faciliter sa compréhension par le programmeur.
L'utilisation incorrecte de pointeurs est une des sources
d'erreurs les plus répandues dans les langages impératifs. Par
conséquent, tout vérificateur de code capable de détecter ce type
d'erreurs à la compilation est le bienvenu. Nous avons proposé
une analyse statique de programmes pour la détection d'accès
incorrects à la mémoire (déréférences de pointeurs invalides)
[10,32]. Un pointeur peut être invalide
parce qu'il n'a pas été initialisé ou parce qu'il désigne une
cellule de la mémoire qui a été désallouée. D'un point de vue
formel, l'analyseur repose sur une sémantique opérationnelle
structurelle du langage, dont nous dérivons une axiomatisation
des propriétés d'accessibilité. Cette axiomatisation est ensuite
raffinée en un analyseur qui est également décrit sous forme de
système d'inférence. La correction de l'analyseur a été démontrée
et sa complexité établie: elle est polynomiale en fonction du
nombre de variables du programme et exponentielle en fonction de
la profondeur d'imbrication des boucles. D'un point de vue
pratique, notre analyseur se distingue de la plupart des
tentatives précédentes par son traitement précis des structures
de données récursives et par son intégration dans un système
interactif. La précision de l'analyse et la possibilité
d'interaction conditionnent en effet l'utilisation d'un tel
analyseur dans un environnement d'aide à la mise au point de
programmes. Notre analyseur est capable en particulier de décrire
exactement le partage induit par des structures complexes comme
les listes circulaires. Les interactions avec l'utilisateur se
font à l'aide d'un langage d'annotations qui s'intègre
naturellement au formalisme de l'analyseur tout en restant très
proche du langage de programmation afin de faciliter sa
compréhension par le programmeur. Les annotations correspondent
soit à des hypothèses ajoutées par l'utilisateur, soit à des
propriétés à vérifier par l'analyseur. Les hypothèses peuvent
être utilisées pour représenter des connaissances sur le contexte
d'exécution du code (ou d'appel d'une procédure) ou pour guider
l'analyseur afin de se focaliser sur certaines parties de
programmes ou certaines valeurs de données. Ces annotations
permettent ainsi d'intégrer de manière naturelle débogage
statique et dynamique (cf. module ).
Une première version de prototype a été réalisée. La partie haute (analyse syntaxique) est écrite à partir d'une version augmentée de Suif empruntée à l'évaluateur partiel Tempo (cf. avant-projet COMPOSE). La génération d'équations récursives est réalisée en SML et la résolution de ces équations repose sur un solveur de points fixes générique développé par ailleurs dans le projet.
Participants : Mireille Ducassé , Sarah Mallet
Mots-clés : Débogage, Explication, Génération et Analyse
de trace, Base de Données Déductive
Résumé : Une base de données déductive est composée de faits (de base) et de règles de déduction déclaratives permettant d'augmenter la connaissance par des faits déduits. Ces règles permettent aux concepteurs de la base de données de se concentrer sur sa logique. Toutefois, les utilisateurs des bases de données ont besoin d'explications pour leur restituer cette logique qui peut être masquée par les détails opérationnels de la mise en oeuvre. Les systèmes d'explication existants rendent à l'utilisateur des arbres de preuve qui donnent une vue éclatée des évaluations. Pour rester plus proche de la notion ensembliste sous-jacente aux bases de données, nous proposons une structure de données, l'arbre DDB, reposant sur des ensembles de substitutions.
Une base de données déductive est composée de deux types de données: des faits de base stockés dans une base de données relationnelle et des données, dites virtuelles, déduites des données de la base à l'aide de règles.
La forme déclarative du langage de règles permet aux
concepteurs de la base de données de se concentrer sur sa logique
(cf. module ).
Toutefois, la mise en oeuvre des bases de données déductives fait
intervenir de nombreuses optimisations afin d'obtenir des
manipulations de données efficaces. De ce fait, les utilisateurs
des bases de données ont besoin de facilités de débogage et
d'explication pour restituer la logique des programmes qui peut
être masquée par les détails opérationnels (cf. module
).
Ces explications sont particulièrement nécessaires dans le contexte des bases de données déductives où les utilisateurs du logiciel doivent comprendre le déroulement des déductions pour en accepter les résultats ou mettre à jour les données, alors qu'ils ne sont pas forcément des informaticiens.
Les systèmes d'explication existants pour les bases de données déductives rendent à l'utilisateur des arbres de preuve. Ces arbres donnent une vision très détaillée de la manipulation des données. Cette présentation éclatée provoque une explosion du nombre d'arbres de preuve produits.
Pour rester plus proche de la notion ensembliste sous-jacente aux bases de données, nous proposons une structure de données, l'arbre DDB, reposant sur des ensembles de substitutions. Elle permet de rassembler les informations éparpillées dans les arbres de preuve et de les présenter de manière plus concise. Les arbres DDB peuvent être montrés aux utilisateurs ou bien analysés par un système d'explications [25,27,26].
Ce travail fait l'objet d'une coopération, pour l'instant informelle, avec une société nouvellement créée, Next Century Media. Cette société fournit un produit reposant sur la technologie des bases de données déductives et vise les applications dans la publicité ciblée. Des discussions sont en cours pour définir les informations qui doivent apparaître dans les traces d'exécution afin de bâtir des explications de plus haut niveau, par exemple l'arbre DDB mentionné précédemment.
Participants : Mireille Ducassé , Erwan Jahier
Mots-clés : Débogage, Analyse dynamique, Traceur abstrait,
Prolog, Mercury
Résumé : Nous avons développé un outil, OPIUM, qui analyse les traces d'exécution de programmes générées par un traceur Prolog existant. Les programmeurs peuvent spécifier de manière précise ce qu'ils veulent observer du comportement du programme à l'aide de requêtes en Prolog. À partir du langage de requêtes, nous avons bâti des analyses qui donnent des vues abstraites des exécutions selon certains critères. Il a également été possible de mettre en oeuvre à faible coût des traceurs abstraits pour des langages de haut niveau. Si le contenu de la trace utilisée et les analyses proposées sont dédiés à Prolog, les techniques de base exploitent une trace dont le seul pré-requis est d'être séquentielle.
Nous avons développé un outil,
OPIUM, qui analyse les traces
d'exécution de programmes générées par un traceur Prolog existant
(cf. module ). Le
programmeur peut poser des questions sur les exécutions à l'aide
de Prolog et de quelques primitives dans une forme concise en
s'appuyant sur la logique et les mécanismes de recherche de
Prolog. Les programmeurs peuvent donc spécifier de manière
précise ce qu'ils veulent voir du comportement du programme.
Ces requêtes peuvent être traitées à la volée ou a posteriori. Dans le cas d'un traitement à la volée, les performances du système permettent d'analyser plusieurs millions d'événements avec des temps de réponse supportables. L'analyse a posteriori nécessite de stocker les événements dans une base de données interne, ce qui prend un temps non négligeable. L'analyse proprement dite, par contre, ne prend pas plus de temps que l'analyse à la volée. Du point de vue de l'utilisateur, les requêtes se posent de la même manière dans les deux cas.
À partir du langage de requêtes, nous avons bâti des analyses qui donnent des vues abstraites des exécutions selon certains critères (par exemple, flot de contrôle ou flot de données). Il a également été possible de mettre en oeuvre à faible coût des traceurs abstraits pour des langages de haut niveau, ce qui a permis de mettre au point facilement des démonstrations d'applications sophistiquées.
Si le contenu de la trace utilisée et les analyses proposées sont dédiés à Prolog, les techniques de base pour mettre en oeuvre le langage de requêtes exploitent une trace qui peut être produite pour n'importe quel langage séquentiel.
Un article récapitule les techniques et les applications, il décrit en particulier de manière détaillée l'interface entre l'analyseur de traces et le traceur [13].
Dans le cadre du projet européen
ARGo (cf. ), nous étudions le débogage
de programmes logiques Mercury, un nouveau langage de
programmation logique développé à l'université de Melbourne,
Australie [SHC96]. Nous
concevons actuellement un traceur pour Mercury. L'objectif à
terme est d'appliquer des techniques proches de celles d'Opium
pour analyser de manière automatique les traces générées.
Participants : Daniel Le Métayer , Valérie-Anne Nicolas ,
Olivier Ridoux , Lionel Van Aertryck
Mots-clés : Test en boîte noire,
Test en boîte blanche, Test structurel, Contrainte, Jeu de test,
Suite de test
Résumé : Nous avons proposé une méthode de génération de suites de tests qui forme le noyau de l'outil CASTING développé en collaboration avec la société AQL. La méthode est indépendante du format d'entrée, ce qui la rend uilisable aussi bien dans le cas du test structurel que fonctionnel. Les suites de tests engendrées dépendent de stratégies spécifiées par l'utilisateur, permettant ainsi d'atteindre la souplesse d'utilisation exigée pour un usage industriel.
Nous avons abordé le problème de la systématisation de la
génération de jeux de test en tentant d'abolir la dichotomie
<<boîte noire/boîte blanche>> (cf. module ).
Pour ce faire, nous décomposons le processus de production des
données de test en trois étapes [30]:
Ces travaux ont conduit au développement de l'outil d'aide à
la génération de jeux de test CASTING
dont un prototype
sera disponible en janvier 1998 [30]. La première version de
CASTING prend en entrée des
spécifications dans la notation AMN
de la méthode B [Abr96] et
fait appel à Ilog Solver
pour résoudre les contraintes
engendrées.
Nous nous intéressons conjointement à la manière d'assurer que des programmes sont conformes à des hypothèses de test: cette assurance peut être obtenue par construction (contrôle a priori), ou par vérification (contrôle a posteriori) quand il s'agit de code existant. Nous considérons dans ce but des schémas de programmes qui peuvent être vus comme une manière de définir des classes de fautes: identifier un programme de manière non ambiguë dans une classe revient à assurer que toutes les fautes ayant pour effet de produire une version de programme dans cette classe seront détectées. Notre objectif actuel est d'associer à tout schéma de programme un type (ou contrainte) caractérisant les jeux de test discriminants pour cette classe.
Nous décrivons dans cette partie les travaux à plus long terme
sur les langages fonctionnels (module ), Gamma Structuré,
les types graphes et les architectures de logiciels (module
).
Participant : Pascal Fradet
Mots-clés : Compilation, Langage fonctionnel,
Transformation de programme
Résumé : De nombreuses techniques ont été proposées pour implanter les langages fonctionnels et il est difficile d'établir des comparaisons rigoureuses entre les différentes options existantes. Nous avons proposé un cadre formel pour décrire et comparer les techniques de compilation des langages fonctionnels. Cela nous a permis d'établir une taxonomie des mises en oeuvre de langages fonctionnels et d'y faire figurer de nombreuses implantations classiques comme la SECD, la Cam, la G-machine, Tim, etc. Nous avons également étudié l'expression d'optimisations standards et la conception de machines hybrides (i.e. intégrant plusieurs choix de compilation).
De nombreuses techniques ont été proposées pour implanter les
langages fonctionnels et il est difficile d'établir des
comparaisons rigoureuses entre les différentes options
existantes. Afin d'apporter des éléments de solution à ce
problème difficile, nous avons proposé un cadre formel pour
décrire et comparer les techniques de compilation des langages
fonctionnels [12]. Nous
avons repris l'idée centrale de nos précédents travaux [6] qui consiste à définir le
processus de compilation comme une suite de transformations de
programmes dans le cadre fonctionnel. Nous avons couvert une
grande partie du domaine en considérant les mises en oeuvre de
l'appel par valeur et l'appel par nécessité, qu'elles soient
basées sur les environnements ou la réduction de graphe. Les
principales tâches d'un compilateur sont la compilation du schéma
d'évaluation, de la -réduction et des
transferts de contrôle (appels et retours de fonctions). Nous
avons identifié pour chaque étape plusieurs choix fondamentaux et
nous les avons décrits comme des transformations de programmes.
La description dans un cadre unique a permis de mettre clairement
en évidence les différences et les similitudes entre la réduction
de graphe et les implantations à environnement.
Un des avantages de cette démarche est de décomposer et de structurer le processus de compilation. On peut, par exemple, montrer que des implantations apparemment très différentes partagent certaines options de mise en oeuvre. Nous avons établi une taxonomie des mises en oeuvre de langages fonctionnels et nous avons classifié de nombreuses implantations classiques comme la SECD, la Cam, la G-machine, Tim, etc. Nous avons également étudié l'expression d'optimisations classiques, la conception de machines hybrides (i.e. intégrant plusieurs choix de compilation) et proposé des comparaisons formelles sous la forme d'étude de complexité des transformations [12]. Ce travail a fait l'objet de la thèse de Rémi Douence, actuellement en séjour post-doctoral à l'université de Carnegie Mellon.
Participants : Pascal Fradet , Julien Mallet , Mario
Südholt
Mots-clés : Schéma de programme, Compilation,
Parallélisme
Résumé : Nous étudions l'utilisation de langages fonctionnels restreints pour la programmation des machines massivement parallèles. La démarche que nous avons adoptée est celle des langages composés d'une collection de schémas de programmes (patrons) intrinsèquement parallèles (map, reduce, scan, etc). Nos travaux dans ce domaine concernent la description abstraite des distributions de données et le calcul précis des coûts de communication.
Le but des langages restreints pour la programmation des machines massivement parallèles est de concilier efficacité, portabilité et abstraction. La démarche que nous avons adoptée est celle des langages composés d'une collection de schémas de programmes (patrons) intrinsèquement parallèles (map, reduce, scan, etc). Les opérateurs parallèles de HPF ou des langages comme NESL peuvent être vus comme des exemples de ce style de programmation. Nous poursuivons deux directions de recherche:
Notre réflexion sur l'introduction d'un système de types pour le langage Gamma nous a conduit à proposer une notion de type graphe correspondant à une classe de graphes d'une même forme. Ces graphes sont manipulés par des réactions qui extraient un sous-graphe selon des conditions locales et le remplacent par un nouveau sous-graphe. Nous avons conçu un algorithme de vérification qui permet d'assurer que le type est préservé par une réaction. Ces types peuvent alors être vus comme des invariants sur la forme des données.
Nous avons proposé une nouvelle version de Gamma, appelée Gamma Structuré, qui intègre les types graphes [14]. Il s'est avéré que les types graphes peuvent apporter des solutions à des problèmes très différents. Nous avons notamment étudié leur application pour la manipulation de structures de données avec partage dans les langages impératifs [19] ainsi que la description d'architectures de logiciels [23,33]. Nous détaillons séparément ces trois domaines d'application.
Participants : Pascal Fradet , Daniel Le Métayer
Mots-clés : Réaction chimique, Multi-ensemble, Structure
de données, Type graphe, Vérification de type, Invariant
Résumé : Nous avons proposé une version de Gamma qui intègre un moyen de définir des données structurées sans pour autant remettre en cause le modèle de calcul de base. Les types graphes permettent une description des structures de données de nature «topologique», sous forme de relations entre les valeurs du multi-ensemble. Ces types peuvent être vus comme des invariants sur les multi-ensembles, le point crucial étant que ces invariants peuvent être prouvés automatiquement. On obtient ainsi une vérification de types pour Gamma Structuré.
Le formalisme Gamma permet une description abstraite des programmes, dépourvue de contraintes d'ordonnancement inutiles. On peut illustrer le comportement des programmes Gamma à l'aide de la métaphore de la réaction chimique : l'exécution est une succession de réactions chimiques consommant les éléments d'un multi-ensemble pour produire de nouveaux éléments selon certaines règles.
Gamma Structuré intègre dans le langage un moyen de définir des données structurées sans pour autant remettre en cause le modèle de calcul de base. La difficulté vient du fait qu'on ne peut recourir à la méthode habituelle pour définir des structures de données (les types récursifs) car ceci induirait un style de programmation récursive (avec une manipulation globale des données) incompatible avec les principes de Gamma. Les types graphes permettent une description des structures de données de nature «topologique», sous forme de relations entre les valeurs du multi-ensemble [14]. Ces types peuvent être vus comme des invariants sur les multi-ensembles, le point crucial étant que ces invariants peuvent être prouvés automatiquement. On obtient ainsi une vérification de types pour Gamma Structuré. Il faut noter que ces types ne contraignent pas les valeurs du multi-ensemble, mais les relations qui les relient. Le bénéfice est double:
Participants : Pascal Fradet , Daniel Le Métayer ,
Florimond Ployette
Mots-clés : Type, Structure de donnée, Vérification,
Pointeur, Sûreté
Résumé : Nous avons proposé une une extension de C intégrant la notion de type graphe. Elle permet de décrire précisément et de manipuler des structures classiques comme les listes circulaires, les listes avec sauts, les arbres binaires avec pointeur parent, etc. Une vérification de type permet d'assurer que la forme d'une structure spécifiée par un type graphe est un invariant du programme. Il devient ainsi possible de détecter de nombreuses erreurs de programmation, ce qui améliore la sûreté des manipulation de pointeurs.
De nombreuses erreurs de programmation ne sont pas détectées par les systèmes de types des langages impératifs. Leur manque d'expressivité les rend incapables, par exemple, de distinguer une liste doublement chaînée d'un arbre binaire. Nous avons proposé une solution à ce problème avec Shape-C, une extension de C intégrant la notion de type graphe (ou «shape type») [19]. On utilise en fait un sous-ensemble des types graphes de Gamma Structuré qui correspond aux structures de données manipulées dans les programmes impératifs. Il devient possible de décrire précisément et de manipuler des structures classiques comme les listes circulaires, les listes avec sauts, les arbres binaires avec pointeur parent, etc. Parmi les avantages de Shape-C on peut citer:
Un pré-processeur, traduisant des programmes Shape-C en C pur après vérification de types, a été réalisé. Il a notamment permis de déterminer plusieurs extensions souhaitables pour faciliter la programmation en Shape-C. Nous travaillons actuellement sur ce point.
Participants : Anne Alexandra Holzbacher , Daniel Le
Métayer , Michaël Périn , Mario Südholt
Mots-clés : Architecture de
logiciel, Coordination, Communication, Style, Vérification
Résumé : Nous avons proposé une manière de spécifier des architectures en terme de graphes. La description d'une application est séparée en deux niveaux bien identifiés: d'une part, l'ensemble de ses entités de base qui représentent des calculs autonomes; d'autre part la coordination de ces entités. Les entités individuelles peuvent être décrites dans des langages traditionnels (par exemple séquentiels) et la coordination est assurée par un composant défini séparément: le coordinateur.
Les principes de base de notre contribution dans le domaine des architectures de logiciels sont les suivants [8]:
L'avantage de cette démarche est de concilier une vision dynamique de l'architecture avec des possibilités de vérification statique.
Ce cadre a été appliqué à un cas d'étude proposé par la société Signaal (Pays-Bas) qui consistait à modéliser un système de contrôle ferroviaire [23,33]. L'architecture ainsi que l'évolution dynamique du système ont pu être décris à l'aide des types graphes. Il a été notamment possible d'assurer statiquement que l'évolution du système respectait les invariants exprimés par la grammaire de graphes. Une implantation en ConCoord a ensuite été dérivée de cette spécification formelle.