Section courante

A propos

Section administrative du site

Les premiers pas

Le code source étant écrit et exécuté sous le Common Language Runtime (CLR) de .NET est appelé «code géré» ou «code managé». Il s'agit d'un type de code spécialement conçu pour être pris en charge, surveillé et optimisé par l'environnement d'exécution de .NET. Le compilateur managé traduit les fichiers de code sources correspondants, comme ceux avec les extensions *.cs (C#) ou *.vb (Visual Basic .NET), en un langage intermédiaire de bas niveau appelé CIL (Common Intermediate Language), étant ensuite accompagné d'un manifeste d'assembly et de métadonnées de type. Ce processus permet d'assurer une compatibilité et une interopérabilité entre différents langages de programmation au sein de l'écosystème .NET.

Ainsi, le MSIL (Microsoft Intermediate Language), étant une autre appellation du CIL, représente un langage intermédiaire central pris en charge par le cadre d'application .NET. Grâce à ce langage, il est possible de créer, construire et compiler des applications .NET, même en utilisant uniquement du code CIL autonome, sans dépendre de langages de haut niveau. Le code MSIL constitue en quelque sorte la structure de base, le squelette, de tout assembly .NET. Plus vous explorez en profondeur les instructions fournies par le MSIL, plus vous développez une compréhension fine et avancée du développement d'applications dans l'univers .NET.

Dans cette page, vous découvrirez une présentation complète et détaillée des ensembles d'instructions MSIL ainsi que leur signification. Cette compréhension sera approfondie à travers l'écriture d'un programme simple utilisant des opcodes MSIL. Vous verrez également comment le compilateur MSIL, nommé ilasm.exe, joue un rôle essentiel en permettant la construction et l'exécution de ce code d'assembly .NET sans passer par le processus classique de compilation offert par l'environnement de développement Visual Studio. Cette approche vous permettra de mieux saisir le fonctionnement interne du CLR et des assemblies .NET.

Les outils de base

Programmer avec des ensembles d'instructions MSIL est assez complexe et considéré comme une tâche ardue, car le développeur utilise directement la grammaire intégrée du CLR, appelée «opcodes», au lieu de la syntaxe anglaise conviviale de C#, F# ou VB.Net. Il est donc conseillé d'installer les outils suivants sur la machine du chercheur lors de ce voyage :

Bien que le code MSIL puisse être créé via l'éditeur simple Notepad, il est recommandé d'écrire du code MSIL à l'aide d'éditeurs à part entière comme SharpDevelop.

Les composantes internes MSIL

Un assembly .NET contient du code MSIL, conceptuellement similaire au bytecode Java, car il n'est compilé en instructions spécifiques à la plateforme que lorsque cela est absolument nécessaire. Le CLR .NET utilise un compilateur JIT pour chaque processeur ciblant l'environnement d'exécution, chacun optimisé pour la plateforme sous-jacente. Les binaires .NET contiennent des métadonnées décrivant les caractéristiques de chaque type du binaire. Ces métadonnées sont officiellement appelées un manifeste, contenant des informations sur la version actuelle de l'assembly, ainsi que la liste de tous les assemblys référencés en externe et des informations sur la culture.

La figure suivante démontre apparemment que chaque code source de programmation autorisé .NET est finalement compilé en MSIL plutôt que directement dans un ensemble d'instructions spécifique. Ce potentiel permet à tous les langages pris en charge par .NET d'interagir entre eux. De plus, le code MSIL offre les mêmes avantages auxquels les professionnels Java sont habitués :

Chaque langage de programmation pris en charge par .NET associe ses mots clefs respectifs aux mnémoniques MSIL. Le code en langage intermédiaire (IL) a tendance à être complexe et totalement incompréhensible. Par exemple, lors du chargement d'une variable chaîne en mémoire, nous n'utilisons pas l'opcode convivial StringLoading, mais plutôt ldstr. Supposons que nous ayons construit l'exemple de programme suivant en langage C# pour comprendre le code généré correspondant derrière les grammaires MSIL.

Le code C# suivant effectue une simple addition de deux valeurs numériques via la méthode testCalcul. Les binaires .NET ne contiennent pas d'instructions spécifiques à la plateforme, mais utilisent un code IL indépendant, généré à l'aide du compilateur C# correspondant (csc.exe) pendant le processus de compilation :

  1. class Program
  2. {
  3.     static void Main(string[] args)
  4.     {
  5.         // Appel de la méthode
  6.         testCalcul(20, 40);
  7.         Console.ReadKey();
  8.     }
  9.  
  10.     // Méthode statique de démonstration
  11.     static void testCalcul(int iPartie1, int iPartie2)
  12.     {
  13.         int Resultat;
  14.         Resultat = iPartie1 + iPartie2;
  15.         Console.WriteLine("Sortie de calcul :: {0}", Resultat);
  16.     }
  17. }

Une fois ce code compilé, le CLR localise et charge ce binaire .NET en mémoire. Vous obtenez ainsi un seul assembly *.exe contenant un manifeste, des métadonnées et des instructions MSIL. Heureusement, le cadre d'application .NET est fourni avec un excellent utilitaire permettant de désassembler tout binaire .NET en son code IL correspondant, appelé «ILDASM.EXE».

Nous pourrions utiliser l'utilitaire ildasm.exe pour désassembler le code IL, soit en mode invite de commande, soit via une représentation graphique classique. Si vous ouvriez cet assembly avec ILDASM.EXE en mode graphique, vous obtiendriez la représentation back-end réelle de chaque instruction de code C# dans l'ensemble d'instructions des opcodes CIL correspondant, comme illustré ci-dessous :

Le fichier ILDASM.EXE charge tout assembly .NET et analyse son contenu, notamment le code MSIL, le manifeste et les métadonnées. Il est généralement capable de récupérer toutes les métadonnées des binaires .NET dans une représentation d'opcode CIL. Double-cliquez sur la méthode de calcul du test (testCalcul) pour examiner le code MSIL généré sous-jacent, comme illustré ci-dessous :

De plus, si vous souhaitez explorer les métadonnées de type pour l'assemblage actuellement chargé, appuyez sur Ctrl+M (View > MetaInfo > Show!) affichant les métadonnées sur la méthode de calcul du test comme dans l'exemple suivant :

Grammaire des opcodes

Le MSIL est un langage de programmation orienté objet complet, comme C#. Il inclut toutes les fonctionnalités classiques de la programmation orientée objet, telles que l'héritage, les classes, les instructions de contrôle, les interfaces, et bien plus encore. Comme mentionné précédemment, il est possible de créer des applications .NET directement en MSIL sans même utiliser l'IDE de Visual Studio. Mais pourquoi la programmation MSIL est-elle si importante à comprendre ? Parce qu'elle aide les développeurs à mieux écrire, déboguer et maintenir leur code. Le tableau 1 présente une brève liste, avec descriptions, de l'ensemble d'instructions MSIL :

Opcode Description
nop Aucune opération n'est effectuée non plus
sub, div, add, mul, rem Effectuer des opérations mathématiques de base
add.ovf Spécifier une valeur entière signée avec contrôle de dépassement
add.ovf.un Spécifier une valeur entière non signée avec vérification de dépassement.
box, unbox Conversion du type de référence en type de valeur et vice-versa
br.s, br Passer à une autre étiquette à un emplacement spécifique
castclass Conversion de type d'une instance vers un type différent
bgt Instruction de branchement de contrôle pour une condition supérieure à.
beg Instruction de branchement de contrôle pour une condition égale à
ble Instruction de branchement de contrôle pour une condition inférieure ou égale à
break Point d'arrêt typique du débogueur
brnull Branche vers la cible si la valeur est nulle
dup Dupliquer la valeur sur la pile
call Appelle la méthode spécifiée
ceq Si valeur1 est égale à valeur2 alors empile sur 1 sinon 0
cgt Si la valeur 1 est supérieure à la valeur 2, empiler sur 1, sinon 0
clt Si la valeur 1 est inférieure à la valeur 2, appuyez sur 1, sinon 0
ldc Charge la valeur des constantes dans la pile mémoire
ldobj Charge la valeur de l'objet dans la pile mémoire
ldstr Charge la valeur de la chaîne dans la pile mémoire
ldarg Charge l'adresse d'un paramètre d'une fonction dans la pile mémoire
arglist Renvoyer la liste des arguments pour la méthode actuelle
readonly Spécifier l'adresse du tableau n'effectue aucune vérification de type lors de l'exécution
starg Entrepose la valeur de la pile dans les listes de paramètres de méthode
stloc Obtenir la valeur actuelle d'une variable à partir de la pile et la copier dans la variable locale
stobj Entreposer une valeur de type dans une adresse mémoire
callvirt Appel d'une fonction virtuelle (VC++, C++/CLI)
brtrue.s Exécution de la branche dans le cas où la condition est différente de zéro ou vraie
brfalse.s L'exécution de la branche continue si la condition est fausse
pop Supprime une valeur du haut de la pile
ret Terminer le flux d'exécution du corps de la méthode
throw Lève une exception
rethrow Renvoie l'exception actuelle
tail Termine l'appel de méthode en cours
volatile Spécifie qu'une référence de pointeur est volatile

De la même manière, le tableau suivant illustre les correspondances de types de données C# correspondent aux types MSIL correspondants :

Type de données MSIL Équivalent C#
int32 int
unsigned int32 uint
int64 long
float32 float
float64 double
bool bool
string string
object object
char char
unsigned int8 byte


Dernière mise à jour : Samedi, le 26 juin 2021