Crypto Week

Implémentation de l’élimination des variables inutilisées et de la détection des variables indéfinies pour le compilateur Solang | par Lucas Steuernagel | Coinmoines | déc. 2021

Implémentation de l'élimination des variables inutilisées et de la détection des variables indéfinies pour le compilateur Solang | par Lucas Steuernagel | Coinmoines | déc. 2021
Photo de Clint Adair sur Unsplash

En juin 2021, j’ai postulé pour un mentorat via la Linux Foundation et j’ai été accepté pour contribuer au compilateur Solang et mettre en œuvre l’élimination des variables inutilisées, la détection des variables non définies et l’élimination des sous-expressions communes. Dans cet article, j’expliquerai mes techniques pour l’élimination des variables inutilisées et la détection des variables indéfinies.

Solang est un compilateur Solidty qui cible Solana, Substrat et ewasm. Il est écrit en Rust et utilise LLVM comme backend. Il nous permet d’écrire de nombreuses optimisations pour le langage Solidity avant d’invoquer LLVM. Solang est à un stade précoce, mais dispose d’une infrastructure de compilateur, telle qu’un arbre d’analyse, un arbre de syntaxe abstraite (AST) et une représentation intermédiaire pour le graphe de flux de contrôle (CFG). Il a également quelques optimisations, telles que le pliage constant et la réduction de la résistance.

Il n’y avait pas de mise en œuvre existante de l’élimination des variables inutilisées et de la détection des variables non définies, c’était donc mon travail en tant que mentoré de mettre cela en œuvre. Le premier objectif était de détecter les variables inutilisées. Chaque variable locale est placée dans une table de symboles (voir l’essentiel Github), afin que nous puissions suivre ses références tout au long du code.

Les variables de stockage, à leur tour, sont clouées à chaque contrat, à l’aide d’un vecteur de variables :

Au cours de l’analyse sémantique de Solang, nous parcourons l’arbre d’analyse et construisons l’arbre de syntaxe abstrait. Chaque fois que nous atteignons un nœud d’expression, nous pouvons détecter toute variable référencée à l’intérieur de l’expression. Par nœud d’expression, je fais référence aux nœuds qui représentent des expressions de base, comme l’addition, la soustraction, le booléen AND, etc. Pour les noms de variables dans les expressions, nous recherchons l’index du nom de variable dans la table des symboles et créons un nœud AST avec une référence à l’indice variable. De même, un nœud AST de variable de stockage contient son index dans le vecteur de variable du contrat.

Ces index de variables nous permettent de récupérer la référence à ces entités, afin que nous puissions signaler que les variables ont été lues à l’intérieur du code Solidity. En utilisant le même raisonnement mentionné ci-dessus, nous pouvons identifier les affectations pendant la traversée de l’arbre d’analyse et tirer parti des nœuds variables pour marquer les variables comme affectées.

La logique d’identification des variables inutilisées fonctionne également pour détecter les définitions d’événements qui n’ont jamais été émises. La principale différence ici est que les événements sont suivis à l’intérieur Espace de noms, à un vecteur d’événements, comme indiqué ci-dessous.

Après l’analyse sémantique, nous parcourons la table des symboles, le vecteur de variable du contrat et le vecteur d’événements de l’espace de noms pour identifier les variables et les événements inutilisés, et émettre des avertissements de compilation.

Il est important de mentionner qu’il y a un cas limite ici. Nous ne pouvons pas marquer les variables de contrat public comme inutilisées car elles sont accessibles directement via une fonction d’accès générée, que nous pouvons appeler dans une transaction blockchain, même si elles ne sont référencées nulle part dans le code Solidity.

Après avoir su exactement quelles variables n’ont jamais été utilisées, nous pouvons implémenter l’élimination des variables inutilisées. Notre code permet à Solang de supprimer les cas suivants de la représentation intermédiaire :

  • Variables qui n’ont jamais été lues ni affectées.
  • Variables qui ont été affectées, mais jamais lues.
  • Affectations des variables inutilisées, y compris les déstructures.
  • Affectations de variables inutilisées à l’intérieur d’expressions (par ex. a = b + (c=1))

Pour ce faire, nous utilisons la traversée AST de Solang lors de la génération de code. Solang a utilisé une telle étape pour créer le graphique de flux de contrôle (CFG). Chaque fois que nous atteignons une déclaration de variable, nous vérifions la table des symboles ou le vecteur de variables du contrat pour vérifier si la déclaration doit être supprimée du CFG, comme le montre l’essentiel suivant :

De même, lorsque nous atteignons un nœud d’affectation, nous vérifions si l’entité à gauche de l’affectation est une variable qui n’a jamais été utilisée. Si c’est le cas, nous pouvons simplement ignorer l’affectation et continuer à créer le CFG. Le cas complexe se produit lorsqu’il y a une affectation dans une expression (par exemple a = b + (c=1)). Solang différencie déjà une instruction d’affectation et une expression d’affectation. La première est la commande d’affectation autonome (par exemple a = b+1), et ce dernier est l’affectation à l’intérieur d’une expression. De cette façon, nous pouvons traiter un cas aussi particulier sans nous soucier des affectations autonomes.

Si l’affectation à l’intérieur d’une expression peut être supprimée, nous l’ignorons et n’ajoutons que le côté droit de l’affectation au CFG. Le côté droit est mêlé à son expression contenante. L’extrait ci-dessous montre l’expression que nous renvoyons au CFG, lorsque nous traitons une expression d’affectation.

Il convient de mentionner que toutes les variables ne peuvent pas être supprimées. Les paramètres de fonction et les variables de retour ne doivent pas être supprimés car ils constituent une partie essentielle de la structure d’un contrat. Les variables de contrat public ne doivent pas non plus être éliminées, car nous pouvons y accéder directement via des fonctions d’accès dans les transactions blockchain.

Solang a déjà une implémentation des définitions d’atteinte qui sert à compléter certaines optimisations spécifiques à Solidity. Nous pouvons l’exploiter pour identifier des variables non définies. Une variable est indéfinie si elle a été déclarée, mais ne s’initialise pas, et cette valeur non initialisée atteint une instruction où la variable est lue.

Pour trouver des utilisations de variables non définies, nous parcourons le CFG et vérifions quelles définitions atteignent les variables référencées à l’instruction que nous analysons. Si une référence n’est pas définie, nous levons une erreur et arrêtons la compilation.

La détection des variables non définies et l’élimination des variables inutilisées ont utilisé l’infrastructure existante de Solang. Le troisième jalon de mon mentorat, l’élimination des sous-expressions communes, est une toute nouvelle passe de compilateur, je la présenterai donc dans un autre article.

Aussi, lisez

Source medium.com

Quitter la version mobile