I) Le byte code

I.1) Introduction

I.2) Structure d'une classe

I.3) Ce que contient le Constant Pool

I.4) Structure d'une méthode

I.5) Format du fichier

I.6) Les instructions de code

I.6.a) Instruction de déclaration de variable locale

I.6.b) Instructions de chargement de valeur

I.6.c) Instructions de sauvegarde de valeur

I.6.d) Instructions de retour

I.6.e) Instructions de calcul

I.6.f) Instructions pour convertir

I.6.g) Instructions pour pousser des constantes

I.6.h) Instruction pour incrémenter

I.6.i) Instructions pour "sauter" dans la méthode

I.6.i.1) Instruction de saut quoi qu'il arrive
I.6.i.2) Instructions conditionnelles comparant à zéro ou null
I.6.i.3) Instructions conditionnelles comparant deux valeurs
I.6.i.4) Instructions de comparaison
I.6.i.5) Instructions de saut switch
I.6.i.6) Instructions de saut de type sous routine

I.6.j) Instructions pour invoqué des méthodes

I.6.k) Instruction pour la création d'objet

I.6.l) Instructions pour les tableaux

I.6.l.1) Instructions pour la création de tableaux
I.6.l.2) Instructions pour lire, écrire dans les tableaux
I.6.l.3) Instruction pour avoir la taille d'un tableau

I.6.m) Instructions de manipulation de la pile

I.6.n) Instructions pour "casté des objets

I.6.o) Instruction pour les exceptions

I.7) Glossaire

I.1) Introduction

Le but ici n'est pas de vous recopier toute la documentation d'Oracle : Spécifications de la JVM, mais de donner suffisamment d'informations pour comprendre le fonctionnement du byte code.
Tout d'abord rappelons que le byte code sont les instructions interprétés par la JVM. Bien sur il s'agit de binaire et on ne va pas taper du binaire à la main.
Pour rendre les chose plus humaines, on va parler des mnémoniques utilisés par la JVM que l'on compilera ensuite en byte code.
Un fichier ".class" contient une classe et une seule (Les classes anonymes et internes sont sorties (Quitte a générer des copies de paramètres) avec la notation $).
Comme ici on veut générer un fichier ".class" correct nous auront une et une seule classe par fichier ou unité de compilation.

I.2) Structure d'une classe

Une classe doit déclarée son nom de manière complète (package et nom).
Bien que le package par défaut (pas de package) soit accepté, nous ne recommandons pas son utilisation, sinon vous pourriez avoir des difficultés pour utiliser votre classe dans certains cas.
Une classe contient contient un ensemble de constantes définis appelé le Constant Pool.
Il faut savoir que la classe fonctionne par référence pour beaucoup de chose et ces références sont stockées dans le Constant Pool. Voir la parties sur le Constant Pool : I.3) Ce que contient le Constant Pool.
Chaque champ, référence à un champ extérieur, référence à une méthode, chaînes de caractères, ... utilisées par la classe sont stockées dans le Constant Pool. Les méthodes se contentent ensuite d'utiliser les références. C'est la JVM qui fait le lien entre la référence et la valeur souhaitée grâce au Constant Pool
Dans le format de fichier que nous avons créé nous avons simplifié l'utilisation du Constant Pool pour le gérer à la compilation. Cette notion est importante pour comprendre certains choix fait pour le format de fichier, le code du compilateur que nous proposons, ou refaire vous même votre propre compilateur
En plus des constantes et des champs dans le Constant Pool une classe contient aussi des méthodes. Voir I.4) Structure d'une méthode pour plus d'information sur les méthodes.

I.3) Ce que contient le Constant Pool

Le Constant Pool peut être vu comme une table d'association (Hash map) entre une référence et une valeur.
Il contient comme valeur : Le Constant Pool est optimiser pour ne pas référencé deux fois la même valeur, si la même chaîne de caractère, le même champs, ... est utilisé plusieurs fois, la même référence est utilisée.

I.4) Structure d'une méthode

Une méthode déclare zéro ou plusieurs paramètres, un type de retour et zéro ou plusieurs exception renvoyés.
Est associé à une méthode une liste de variable locales qui contient une référence sur this (Si la méthode n'est pas statique), les paramètres de la méthodes, ainsi que toutes les variables locales à la méthode.
L'ordre de la liste est toujours this (Présent pour les méthodes non statiques), premier paramètre, second paramètre, ..., dernier paramètre, première variable locale déclarée, seconde variable locale déclarée, ..., dernière variable locale déclarée.
La méthode contient du code. Le code est une série d'instructions avec paramètres, voir Instructions comprises par le byte code
A l’exécution de méthode est créée une pile d'exécution. Cette pile, vide au départ, est utilisée par les différentes instructions pour écrire et/ou lire des informations. Attention la lecture retire l'information de la pile. C'est pour cela qu'il est important de connaître ce que lit et écrit une instruction sur la pile. Car si l'instruction ne trouve pas l'information dont elle à besoin sur la pile, un crash aura lieu.
En plus de la compilation nous avons ajouté un inspecteur de pile qui va vérifier a l'avance si la pile semble correctement utilisée. Il n'est pas parfait et peut laisser certaines erreurs d'utilisation passée, mais permet d'éviter la plus part.
Choses auxquelles vous devez faire attention (Sinon crash à l'exécution) : Par anticipation au point suivant et pour illustré notre propos, voici le code minimal d'une méthode : method minimal { RETURN }

I.5) Format du fichier

Pour saisir du code, il fallait décidé d'un format de fichier pour compiler, voilà ce que nous proposons, il évite de gérer soit même la Constant Pool et tiens compte des contraintes de la déclaration d'une classe.
Rappel, un fichier doit contenir une classe et une seule, donc rien ici ne permet de classe anonyme ou interne.
Pour le moment : Ces limitations sont du que nous avons préférés nous concentrés sur le reste.
Le format impose une instruction par ligne et {, } sont des instructions à part entière.
Les commentaires sont soit //, ; ou bien le commentaire sur plusieurs lignes : /* ... */. Exemples de commentaires : ... // This is a comment ALOAD this ; Obtain reference on this /* Comment on several lines */ ILOAD /* comment */ param1 ... Dans la suite nous faisons comme le compilateur, nous ignorons les commentaires et les lignes vides, mais comme d'habitude elles peuvent être n'importe où.
Le fichier doit commencer par la déclaration de la classe grâce au mot clef class. Il doit être suivit du nom complet de la classe (le package avec),.
Exemple : class jhelp.asm.Test Pour créer la classe Test dans le package jhelp.asm
Viens ensuite une instruction facultative, mais bien pratique, l'instruction import. Comme en Java il permet ensuite d'utiliser la classe avec son nom court.
Exemple : import java.util.List Vous remarquerez au passage que le ; n'est pas obligatoire, car il n'y a qu'une instruction par ligne.
La résolution d'une classe ce fait ainsi :
SI le nom de la classe est complet (Avec le package)
ALORS on utilise celui-ci
SINON SI il se trouve dans la liste des imports
ALORS on utilise celui des imports
SINON SI il se trouve dans "java.lang"
ALORS on utilise celui de "java.lang"
SINON On le considère du même package que la classe courante
Pour étendre d'une autre classe l'instruction est extends suivit du nom de la classe. Le nom court est possible voir Les règles de résolution des classes.
Exemple : class jhelp.example.MyPanel import javax.swing.JPanel extends JPanel Comme en Java, un seul extends est possible et si il n'est pas présent la classe hérite de java.lang.Object
Pour implémenter une ou plusieurs interface, l'instruction implements suivit du nom de l'interface. Le nom court est possible voir Les règles de résolution des classes.
Une seule implémentation par ligne, si il y a plusieurs interfaces il faudra autant de ligne avec l'instruction implements.
Exemple : implements Comparable implements Iterable Bien entendu votre classe devra ensuite respecter les contrats de la classe parent et des interfaces implémentées.

Une fois la classe déclarée (Obligatoire), les imports faits (Optionnel), la casse parent précisé (Optionnel), les interfaces à implémentées précisée (Optionnel), on peut déclarer les champs de la classes et les références au champs d'objet extérieurs.

Déclaré un champ de la classe l'instruction field suivit du type et du nom.Exemple : field int age field String name field boolean alive Pour faire référence à un champ extérieur, l'instruction field_reference suivit de la classe où se trouve le champ, suivit du type du champ, suivit de son nom dans la classe, suivit d'un alias pour l'utiliser. Exemple : class jhelp.example.Print import java.io.PrintStream field_reference System PrintStream out systemOut field_reference System PrintStream err systemErr
Maintenant tout est prêt pour écrire les méthodes, puisque toutes les références sont prêtes.
Pour déclarer une méthode, l'instruction method suivit du nom de la méthode.
Pour ajouter des paramètres à la méthode, l'instruction parameter suivit du type suivit du nom.
Pour déclarer le type de retour de la méthode (Absent pour les méthodes void), l'instruction return suivit du type de retour
Pour déclarer une exception levée par la méthode, l'instruction throws suivit du nom de la classe
Pour commencer le code de la méthode l'instruction {. Il s'agit d'une instruction à part entiére et doit être seul sur sa ligne (sauf commentaire bien entendu)
Pour terminer le code de la méthode l'instruction }. Il s'agit d'une instruction à part entiére et doit être seul sur sa ligne (sauf commentaire bien entendu)
Exemple : class jhelp.example.Add //Addition of 2 integers method add parameter int first ; First integer parameter int second ; Second integer return int { ILOAD first ; Push first integer in the stack : [] => [first] ILOAD second ; Push second integer in the stack : [first] => [first, second] IADD ; Add the 2 last number of the stack and push the result : [first, second] => [result] IRETURN ; Take last element of the stack and return it : [result] =>[] } Nous n'avons pas encore parler des instructions de code, mais nous espérons le code ci-dessus suffisamment simple et commenté pour comprendre ce qui ce passe.

I.6) Les instructions de code

Les instruction de code sont le code tapé et exécuté pour chaque méthode. Les instructions sont directement interprétés par la JVM.
Note pour ceux qui voudraient faire le propre compilateur, quand ici je parlerais de nom de champs, d'appel de méthode, de constante, il faut comprendre référence dans la Constant Pool. Et quand je parle de nom de paramètre, de this ou de variable locale à la méthode, il faut comprendre indice dans la liste des variables locales. Il est aussi à noter que la notion de label n'existe pas, il faut se débrouiller pour calculer la zone mémoire de destination pour les GOTO, IF...
C'est aparté faite, rentrons dans le vif du sujet.
Première chose à savoir et a garder en tête, le byte code est ultra typé. Ce qui veut dire qu'on ne peut pas directement additionner un double et un int par exemple, il faut d'abord convertir le int en double pour pouvoir faire l'addition.
Autre chose à savoir sur les types, les boolean sont vus comme des byte de valeurs 0 (Pour false) ou 1 (Pour true).
Encore sur les types, les byte, char et short sont convertis en int quand ils sont poussé sur la pile d'exécution. Et quand un int est lu de la pile pour être stocké dans un byte, char ou short, il est d'abord tronqué.
C'est pour cela que les instructions qui lisent des int sur la pile fonctionneront même si il a été poussé des byte, char ou short, puisque sur la pile ils ont été convertis en int. L'inverse étant aussi vrai puisque les int peuvent être tronqués.

I.6.a) Instruction de déclaration de variable locale

Cette instruction n'existe pas le byte code, si bien, qu'il peut paraître étrange de commencer par celle-ci, mais elle pratique et évitera de vous poser la question : Ils parlent des variables locales à la méthode, mais comment fait-on pour en créer une ?
L'instruction est VAR suivit du type, suivit du nom. Exemple : VAR double salary VAR String name

I.6.b) Instructions de chargement de valeur

Ces instructions permettent de récupérer soit des variables locale (this, paramètre ou déclarée avec VAR), soit des champs (interne à la classe ou externe) et de stocker leur valeurs dans la pile d'exécution.
Les instruction ?LOAD (ALOAD, ILOAD, DLOAD, FLOAD et LLOAD) s’occupent des variables locale (this, paramètre ou déclarée avec VAR)
La première lettre de l'instruction indique le type que doit avoir la variable locale (this, paramètre ou déclarée avec VAR) a récupérer.
Comme toutes les instruction typées qui vont suivre A signifie référence sur un objet, I un int, D un double, F un float et L un long.
L'instruction ?LOAD (ALOAD, ILOAD, DLOAD, FLOAD ou LLOAD) est simplement suivit du nom de la variable locale (this, paramètre ou déclarée avec VAR) a charger.
Petit rappel this est réservé et indique l'objet lui même et ne fonctionnera donc qu'avec ALOAD.
Exemple : method test parameter int p1 { VAR double p2 DCONST 0 ; push 0(double) on stack : [] => [0(double)] //Explain later, here just for code execute DSTORE p2 ; Store stack value on p2 : [0(double)] => [] //Explain later, here just for code execute ALOAD this ; Load 'this' reference on stack : [] => [this(Object_reference)] ILOAD p1 ; Load int p1 on stack : [this] => [this(Object_reference), p1(int)] DLOAD p2 ; Load double p2 on stack : [this(Object_reference), p1(int))] => [this(Object_reference), p1(int), p2(double)] RETURN ; Return is mandatory ; [this(Object_reference), p1(int), p2(double)] => [] } Effet sur la pile d'exécution de l'instruction : ajoute la valeur de la variable locale à la fin de la pile.
Comme indiqué plus haut ALOAD pousse une référence sur un objet, ILOAD un int, LLOAD un long, FLOAD un float et DLOAD un double

Pour les champs maintenant, il existe deux instructions : GETFIELD, pour les champs non statique et GETSTATIC pour les champs statiques.
Avant d'utiliser l'une ou l'autre vous devez donc savoir si le champ à atteindre est statique ou non.
Elles demandent toutes les deux d'être suivit du nom du champ (Pour les champ de la classe) ou de son alias (Pour les champs extérieurs).
Pour savoir sur qu'elle instance d'objet le champ doit être lu GETFIELD le lit sur la pile (Rappel lire sur la pile veut aussi dire le retirer). Tandis queGETSTATIC avec le nom ou l'alias à tout ce qu'il faut et ne lit donc rien sur la pile.
Après exécution, ces deux instructions écrivent sur la pile la valeur du champ.
Pour l'exemple on va avoir besoin d'une autre classe à qui on va accéder aux champs : package jhelp.example public class FieldVisible { public static String fieldStaticString; public int fieldInt; // ... } Maintenant voici un petit exemple : class jhelp.test.TestField import jhelp.example.FieldVisible field double myField field_reference FieldVisible String fieldStaticString FieldVisible_fieldStaticString field_reference FieldVisible int fieldInt FieldVisible_fieldInt method test parameter FieldVisible fieldVisible { //Read : myField ALOAD this ; Push this on stack : [] => [this(object)] GETFIELD myField ; Get myField on this : [this(object)] => [myField(double)] //Read : FieldVisible.fieldStaticString GETSTATIC FieldVisible_fieldStaticString ; Get fieldStaticString : [myField(double)] => [myField(double), fieldStaticString(object)] //Read : fieldVisible.fieldInt ALOAD fieldVisible ; Push fieldVisible : [myField(double), fieldStaticString(object))] => [myField(double), fieldStaticString(object), fieldVisible(object)] GETFIELD FieldVisible_fieldInt ; Get fieldInt : [myField(double), fieldStaticString(object), fieldVisible(object))] => [myField(double), fieldStaticString(object), fieldInt(int)] //The end RETURN ; Mandatory : [myField(double), fieldStaticString(object), fieldInt(int)] => [] } Chose à se rappeler : avant d'appeler GETFIELD s'assurer qu'en fin de pile il y ait la référence de l'objet qui détient le champs.

I.6.c) Instructions de sauvegarde de valeur

Nous allons faire maintenant l'opération inverse que précédemment : écrire une valeur sur une variable locale (Paramètre ou déclaré avec VAR) ou un champs.
Pour écrire dans une variable locale (Paramètre ou déclaré avec VAR) on utilise les instructions ?STORE (ASTORE, ISTORE, LSTORE, FSTORE et DSTORE).
Comme pour les ?LOAD, A pour référence sur un objet objet, I un int, L un long, F un float et D un double.
Les instructions ?STORE sont suivit du nom de la variable locale dans laquelle écrie. Elles prennent de la pile d'exécution la valeur à écrire : method test parameter byte p1 { VAR String name // ..... ; Some instructions have pushed values on stack : ... => [... , object, int] ISTORE p1 ; Give at p1 value on stack : [..., object, int] => [..., object] ASTORE name ; Give at name value on stack : [..., object] => [...] // ..... RETURN }
Pour les champs les instructions sont PUTFIELD pour les champs non statiques et PUTSTATIC pour les champs statique.
Elles sont suivent par le nom ou alias du champs à écrire.
PUTFIELD s'attend à lire sur la pile la référence sur l'instance de l'objet et la valeur à écrire. Bref il lui faut que la pile finisse par [..., référence sur l'instance, valeur à donnée].
PUTSATIC va seulement lire la valeur à écrire sur la pile. Il s'attend donc qu'elle finisse par : [..., valeur à donnée].
Reprenons la classe Java de toute à l'heure : package jhelp.example public class FieldVisible { public static String fieldStaticString; public int fieldInt; // ... } Et maintenant un petit exemple : class jhelp.test.TestField import jhelp.example.FieldVisible field double myField field_reference FieldVisible String fieldStaticString FieldVisible_fieldStaticString field_reference FieldVisible int fieldInt FieldVisible_fieldInt method test parameter FieldVisible fieldVisible parameter String name parameter int age parameter double real { // this.myField = real ALOAD this ; Push this : [] => [this(object)] DLOAD real ; Push real : [this(object)] => [this(object), real(double)] PUTFIELD myField ; Store in myField : [this(object), real(double)] => [] // fieldVisible.fieldInt = age ALOAD fieldVisible ; Push fieldVisible : [] => [fieldVisible(object)] ILOAD age ; Push age : [fieldVisible(object)] => [fieldVisible(object), age(int))] PUTFIELD FieldVisible_fieldInt ; Store in fieldVisible.fieldInt : [fieldVisible(object), age(int))] => [] // FieldVisible.fieldStaticString = name ALOAD name ; Push name : [] => [name(object)] PUTSTATIC FieldVisible_fieldStaticString ; Store in FieldVisible.fieldStaticString : [name(object)] => [] RETURN ; Mandatory : [] => [] } On se rend compte rien que dans cet exemple de l'importance de bien faire attention à l'état de la pile avant d'appeler une instruction.

I.6.d) Instructions de retour

Comme indiqué dans I.4) Structure d'une méthode une méthode doit toujours se terminer avec une instruction de retour.
Pour les méthodes sans valeur de retour (void), on peut le deviner à travers les précédent exemples, c'est RETURN.
Pour les méthodes qui retourne une valeur un ?RETURN (ARETURN, IRETURN, LRETURN, FRETURN et DRETURN) leur ait consacré.
La même logique que précédemment est appliquée : ARETURN pour retourné un objet, IRETURN un int, LRETURN un long, FRETURN un float et DRETURN un double.
Les instructions ?RETURN n'ont pas de paramètres. Elles lisent sur la pile la valeur a retourner.
Exemple : method test //... return short { // ..... // .... [...] => [..., int] IRETURN }

I.6.e) Instructions de calcul

Pour la suite le ? devant les instructions pourra être remplacer par I, L, F ou D. I pour les opérations sur les int, L les long, F les float et D les double.
Nous rappelons également que ces opérations doivent se faire avec le type demandé et seulement celui-ci. Impossible d'additionner un int avec un float par exemple.
Il existe des instructions de conversion d'un type à l'autre que nous verrons dans le point suivant.
Les instructions ?ADD : lisent deux nombres de la pile, les additionnent et poussent le résultat sur la pile.
Les instructions ?DIV : lisent deux nombres de la pile, les divisent et poussent le résultat sur la pile. Attention aux divisions par 0
Les instructions ?MUL : lisent deux nombres de la pile, les multiplient et poussent le résultat sur la pile.
Les instructions ?SUB : lisent deux nombres de la pile, les soustraient et poussent le résultat sur la pile.
Les instructions ?REM : lisent deux nombres de la pile, calculent le reste de la division (modulo) et poussent le résultat sur la pile. Attention aux divisions par 0
Les instructions ?NEG : lisent un nombres de la pile, prennent l'opposé (multiplient par -1) et poussent le résultat sur la pile.
class jhelp.example.Operations //Return : first + second method addition parameter int first parameter int second return int { ILOAD first ; Push first on stack : [] => [first(int)] ILOAD second ; Push second on stack : [first(int)] => [first(int), second(int)] IADD ; Add 2 number on stack and push result : [first(int), second(int)] => [first+second(int)] IRETURN ; Return last element on stack : [first+second(int)] => [] } //Return : first - second method subtraction parameter float first parameter float second return float { FLOAD first ; Push first on stack : [] => [first(float)] FLOAD second ; Push second on stack : [first(float)] => [first(float), second(float)] FSUB ; Subtract 2 number on stack and push result : [first(float), second(float)] => [first-second(float)] FRETURN ; Return last element on stack : [first-second(float)] => [] } //Return : first / second method divide parameter double first parameter double second return double { DLOAD first ; Push first on stack : [] => [first(double)] DLOAD second ; Push second on stack : [first(double)] => [first(double), second(double)] DDIV ; Divide 2 number on stack and push result : [first(double), second(double)] => [first/second(double)] DRETURN ; Return last element on stack : [first/second(double)] => [] } //Return : first * second method multiply parameter long first parameter long second return long { LLOAD first ; Push first on stack : [] => [first(long)] LLOAD second ; Push second on stack : [first(long)] => [first(long), second(long)] LMUL ; Multiply 2 number on stack and push result : [first(long), second(long)] => [first*second(long)] LRETURN ; Return last element on stack : [first*second(long)] => [] } //Return : first % second method modulate parameter int first parameter int second return int { ILOAD first ; Push first on stack : [] => [first(int)] ILOAD second ; Push second on stack : [first(int)] => [first(int), second(int)] IREM ; Modulate 2 number on stack and push result : [first(int), second(int)] => [first%second(int)] IRETURN ; Return last element on stack : [first%second(int)] => [] } //Return : -number method negate parameter double number return double { DLOAD number ; Push number on stack : [] => [number(double)] DNEG ; Negate number on stack and push the result : [number(double)] => [-number(double)] DRETURN ; Return last element on stack : [-number(double)] = > [] }
Les instructions suivantes sont des opérations binaires (bit à bit) et ne concernent que les int ou long.
Si bien qu'à partir d'ici le ? ne pourra être remplacé que par I ou L.
?AND lit deux nombres de la pile, fait le ET logique (bit à bit), et écrit le résultat sur la pile.
?OR lit deux nombres de la pile, fait le OU logique (bit à bit), et écrit le résultat sur la pile.
?XOR lit deux nombres de la pile, fait le OU BIEN logique (bit à bit), et écrit le résultat sur la pile.
Note : pour le NON logique il suffit d'utiliser ?NEG ;)

?SHL lit deux nombres de la pile. Le premier nombre est du type demandé (int ou long) et le second toujours un int positif ou nul. Décale vers la gauche le premier du nombre de bit indiqué par second. Écrit le résultat sur la pile.
?SHR lit deux nombres de la pile. Le premier nombre est du type demandé (int ou long) et le second toujours un int positif ou nul.Décale vers la droite le premier du nombre de bit indiqué par second. Écrit le résultat sur la pile.
?USHR lit deux nombres de la pile. Le premier nombre est du type demandé (int ou long) et le second toujours un int positif ou nul. Décale vers la droite le premier du nombre de bit indiqué par second. Écrit le résultat sur la pile.
La différence entre ?SHR (Équivalent Java >>) et ?USHR (Équivalent Java >>>) et que ?SHR va répéter le premier bit, tandis que ?USHR va toujours mettre 0. Si bien que sur les nombres positifs le résultat sera le même. Par contre sur les nombre négatifs on peut voir la différence. . Dans le tableau ci-dessous on considère des entiers (L'idée est la même pour des long, mais la version binaire est plus longue à écrire).
ExpressionBinaireDécimal
number11111111111111111111111111001001-55
shift000000000000000000000000000000113
ISHR number shift11111111111111111111111111111001-7
IUSHR number shift00011111111111111111111111111001536870905
ISHL number shift11111111111111111111111001001000-440
IAND number shift000000000000000000000000000000011
IOR number shift11111111111111111111111111001011-53
IXOR number shift11111111111111111111111111001010-54

I.6.f) Instructions pour convertir

Pour passer d'un type de nombre à l'autre il faut se rappeler que I signifie int, L signifie long, F signifie float et D signifie double.
Ensuite si on veut convertir un int en double, par exemple, on utilisera l'instruction I2D, un float en long : F2L ,...
Bien sur, la perte d'information, de précision peut apparaître lors d'une conversion, comme en Java.
Aucune de ces instructions n'a de paramètres. Elles lisent sur la pile le nombre à convertir et écrivent la conversion sur la pile.

Il est possible de tronqué un int en byte, char ou short de manière explicite.
Ces instructions n'ont pas de paramètres, lisent un int sur la pile et écrivent la version toquée sur la pile, mais du point de vu de la pile ça reste des int.
I2B tronque en byte, I2C tronque en char et I2S tronque en short.

I.6.g) Instructions pour pousser des constantes

Il est pratique de pouvoir pousser des constantes dans la pile pour initialiser une variable, ajouter un nombre fixe, mettre un objet à null, ...

Tout d'abord les constantes optimisées. Optimisée car elles ne requiert pas d'avoir leur valeur stockées dans la Constant Pool et d'utiliser leur référence. Non ces instructions vont directement poussés la valeur dans la pile sans rien àavoir à résoudre.
Elles sont donc très rapide d'exécution. Par contre elles sont limitées quand aux valeurs qu'elles peuvent poussées.
L'instruction ACONST_NULL pousse la référence d'objet null sur la pile.
L'instruction DCONST suivit de 0 ou 1, pousse le double 0 ou 1 sur la pile. Seuls 0 ou 1 sont possible.
L'instruction FCONST suivit de 0, 1 ou 2, pousse le float 0, 1 ou 2 sur la pile. Seuls 0, 1 ou 2 sont possible.
L'instruction ICONST suivit de -1, 0, 1, 2, 3, 4 ou 5, pousse le int -1, 0, 1, 2, 3, 4 ou 5 sur la pile. Seuls -1, 0, 1, 2, 3, 4 ou 5 sont possible.
L'instruction LCONST suivit de 0 ou 1, pousse le long 0 ou 1 sur la pile. Seuls 0 ou 1 sont possible.

Moins optimisés, mais utiles : L'instruction BIPUSH suivit d'un entier entre -128 et 127, pousse le int sur la pile. Seules des valeurs entre -128 et 127 sont possible.
L'instruction SIPUSH suivit d'un entier entre -32768 et 32767, pousse le int sur la pile. Seules des valeurs entre -32768 et 32767 sont possible.
L'instruction PUSH suivit d'une constante, pousse la constante sur la pile.
Le type de la constante dépend du paramètre donné à PUSH. La convention choisit ici est proche du Java :
ExpressionType poussé sur la pileInformation
trueint1 est poussé
falseint0 est poussé
'a'intLe code ASCII de du caractère 'a' est poussé. Comme en Java le caractère doit être entre simple guillemet.
123int123 est poussé
12.34ffloat12.34 est poussé. Il faut ajouté à la fin du nombre un F minuscule ou majuscule pour précisé qu'on veut poussé un float
1234Llong1234 est poussé. Il faut ajouté à la fin du nombre un L minuscule ou majuscule pour précisé qu'on veut poussé un long
12.34double12.34 est poussé. Si le nombre peut être confondu avec un int (comme 1569), il faut ajouté à la fin du nombre un D minuscule ou majuscule pour précisé qu'on veut poussé un double
"Chaîne d'exemple !"java.lang.StringEnregistre la chaîne de caractères dans la Constant Pool et pousse une référence sur l'objet dans la pile. Comme en Java, les chaînes sont entre double guillemet
Les instructions LDC et LDC_W font la même chose que PUSH mais n'accepte pas les long, ni les double.
L'instruction LDC2_W est dédié aux long ou double.
En réalité l'instruction PUSH n'existe pas dans le byte code, elle est remplacée par la bonne instruction à la compilation.Notre compilateur va même cherché à la remplacer par une des instructions optimisées citées plus haut si possible

I.6.h) Instruction pour incrémenter

Pour faire l'équivalent de : i += 5; j -= 3; Si i et j sont des variables locales de type int, avec ce que nous avons vu on ferait : ILOAD i ; Push i on stack : [...] => [..., i(int)] ICONST 5 ; Push 5 on stack : [..., i(int))] => [..., i(int), 5(int)] IADD ; Add 2 numbers on stack : [..., i(int), 5(int)] => [..., i+5(int)] ISTORE i ; Store stack value on i : [..., i+5(int)] => [...] ILOAD j ; Push j on stack : [...] => [..., j(int)] ICONST 3 ; Push 3 on stack : [..., j(int))] => [..., j(int), 3(int)] ISUB ; Subtract : [..., j(int), 3(int)] => [..., j-3(int)] ISTORE j ; Store stack value on j : [..., j-3(int)] => [...] Mais il existe une instruction plus optimisé pour ce cas : IINC.
IINC est suivit du nom de la variable locale à modifié, suivit de la valeur d'incrément entre -128 et 127.
IINC ne modifie pas la pile.
Notre exemple devient : IINC i 5 ; i += 5 : [...] => [...] IINC j -3 ; j -= 3 : [...] => [...] IINC ne fonctionne que sur des variables locales de type int avec un incrément constant compris entre -128 et 127.

I.6.i) Instructions pour "sauter" dans la méthode

Les instructions de "saut" sont utiles pour se déplacer au sein de la méthode pour ne pas exécuté l'instruction suivante, mais aller à une instruction plus loin ou retourné en arrière.
Elles sont de trois natures différentes.
Pour plus de clarté nous allons découper les instructions conditionnelles en sous familles : Chaque condition de saut à besoin d'un point d'atterrissage. C'est à dire où dans le code on doit sauter.
Pour éviter de devoir recalculer à chaque fois que le code change le point d'arriver, nous avons ajouter l'instruction LABEL.
LABEL est suivit d'un nom. Ce nom servira aux différente instructions de saut pour indiquer où atterrir.
Bien sûr il ne peut pas, au sein de la même méthode, avoir deux LABEL avec le même nom.
Comme le LABEL doit s'attacher à une vraie instruction on ne peut pas avoir deux définitions de LABEL à la suite (Ou séparé que par des VAR). Ce n'est pas une si grande contrainte car de toute façon ils pointeraient au même endroit et seul suffit dans ce cas.
Maintenant que nous pouvons définir un point d'atterrissage, on peut utiliser les instructions de saut.

I.6.i.1) Instruction de saut quoi qu'il arrive

L'instruction GOTO suvit du nom du label où il doit sauté saute toujours.
Cette instruction ne modifie pas la pile.
Il faut évidemment faire attention à l'état de la pile pour que l'instruction suivant LABEL puisse s'exécuter correctement.
// ... ILOAD j ; push j : [...] => [..., j(int)] GOTO further ; Jump to further : [..., j(int)] => [..., j(int)] ILOAD p ; Ignored by the jump IADD ; Ignored by the jump LABEL further ; We reach the instruction bellow ISTORE k ; Store the stack int to k : [..., j(int)] => [...] // .....

I.6.i.2) Instructions conditionnelles comparant à zéro ou null

Celles qui comparent à null sont suivit du label où sauter.
Elles lisent sur la pile une référence sur l'objet à comparé à null.
L'instruction IFNONNULL va sauter au label indiqué si la référence sur l'objet lue n'est pas null.
L'instruction IFNULL va sauter au label indiqué si la référence sur l'objet lue est null.

Les comparaisons à zéro sont suivit du label où sauté si la condition est vérifiée.
Elles lisent un int sur la pile qu'elle compare à zéro pour savoir si elles doivent sauter ou pas au label indiqué.
InstructionCondition de saut
IFEQSi le int lu sur la pile est égal à zéro
IFGESi le int lu sur la pile est plus grand ou égal à zéro
IFGTSi le int lu sur la pile est strictement plus grand que zéro
IFLESi le int lu sur la pile est plus petit ou égal à zéro
IFLTSi le int lu sur la pile est strictement plus petit que zéro
IFNESi le int lu sur la pile est différent de zéro
//Divide two number //If second number is 0 then 0 is return, else returns first/second method divideNoZero parameter int first parameter int second return int { ILOAD second ; Push second : [] => [second(int))] IFEQ zeroTreatment ; Compare stack to 0 : [second(int))] => [] ; Reach if second not 0 ILOAD first ; Load first : [] => [first(int)] ILOAD second ; Load second : [first(int)] => [first(int), second(int)] IDIV ; Divide : [first(int), second(int)] => [first/second(int)] IRETURN ; Return stack element : [first/second(int)] => [] LABEL zeroTreatment ; Reach if second is 0 ICONST 0 ; Push 0 : [] => [0(int)] IRETURN ; Return stack element : [0(int)] => [] }

I.6.i.3) Instructions conditionnelles comparant deux valeurs

La comparaison entre deux références d'objet est comme le == ou le != de Java, il ne s'agit de comparé que les références (Si elles pointent sur la même zone mémoire).
Elles sont suivit du label où sauté.
Elles lisent deux références d'objet sur la pile et sautent selon leur comparaison.
L'instruction IF_ACMPEQ va sauter si les références sont égales.
L'instruction IF_ACMPNE va sauter si les références sont différentes.

La comparaison entre int est possible.
Ces instructions sont suivit du label où sauté.
Elles lisent sur la pile les deux int à comparer et sautent selon le résultat.
En admettant que l'état de la pile soit [..., first(int), second(int)] au moment de leur appel :
InstructionCondition de saut
IF_ICMPEQfirst == second
IF_ICMPGEfirst >= second
IF_ICMPGTfirst > second
IF_ICMPLEfirst <= second
IF_ICMPLTfirst < second
IF_ICMPNEfirst != second
//Minium of two number method min parameter int first parameter int second return int { ILOAD fisrt ; Push first : [] => [first(int)] ILOAD second ; Push second : [first(int)] => [first(int), second(int)] IF_ICMPGT minimumSecond ; Jump if first>second : [first(int), second(int)] => [] ; Reach if first <= second ILOAD first ; Push first : [] => [first(int)] IRETURN ; Return first : [first(int)] => [] LABEL minimumSecond ; Reach if first > second ILOAD second ; Push second : [] => [second(int)] IRETURN ; Return second : [second(int)] => [] }

I.6.i.4) Instructions de comparaison

Ce ne sont pas des instructions de saut, mais elles vont nous permettre de comparer deux nombres et permettront éventuellement de faire un saut selon le résultat.
Ceci nous permettra, entre autre, d'écrire la précédente méthode minimum pour des double.
Ces instructions n'ont pas de paramètres.
Elles lisent deux nombres sur la pile et écrivent un int valant -1, 0 ou 1.
Le type attendu des nombres sur la pile dépend comme d'habitude de la première lettre (D double, F float, L long). Si la pile est [..., first, second] alors :
ConditionValeur int écrite
first < second-1
first == second0
first > second1
Les instructions sont : DCMPG, DCMPL, FCMPG, FCMPL et LCMP. La différence entre DCMPG (respectivement FCMPG) et DCMPL (respectivement FCMPL) vient de la façon dont NaN est traité.
DCMPG et FCMPG vont poussé 1 (int) sur la pile si au moins un des deux nombre vaut NaN.
DCMPL et FCMPL vont poussé -1 (int) sur la pile si au moins un des deux nombre vaut NaN.
//Minium of two number method min parameter double first parameter double second return double { DLOAD fisrt ; Push first : [] => [first(double)] DLOAD second ; Push second : [first(double)] => [first(double), second(double)] DCMPG ; Compare first, second : [first(double), second(double)] => [int] IFGT minimumSecond ; Jump if > 0 : [int] => [] ; Reach if first <= second DLOAD first ; Push first : [] => [first(double)] DRETURN ; Return first : [first(double)] => [] LABEL minimumSecond ; Reach if first > second DLOAD second ; Push second : [] => [second(double)] DRETURN ; Return second : [second(double)] => [] }

I.6.i.5) Instructions de saut switch

Les instructions LOOKUPSWITCH et TABLESWITCH s'écrivent de la même façon.
D'un point de vu optimisation, TABLESWITCH est la plus optimisé, mais doit respecté des conditions strictes.
Pour se simplifier on peut utiliser l'instruction SWITCH qui sera remplacé à la compilation par TABLESWITCH si possible sinon par LOOKUPSWITCH .
Ces instructions ont un nombre impair de paramètres. Se sont des couples de int et label et le dernier un label.
Ces instructions vont lire un int et aller au label correspondant. Si aucun couple ne convient il va sauter au dernier label que fait figure de choix par defaut.
// ...... : [...] => [..., int] SWITCH 0 zero 1 one 5 five default ; Switch : [..., int] => [...] //... LABEL zero ; Reach here if tested int was 0 //... LABEL one ; Reach here if tested int was 1 //... LABEL five ; Reach here if tested int was 5 //... LABEL default ; Reach here if tested int wasn't 0, 1 nor 5 //... Les conditions que TABLESWITCH doit respecté : A noter que le label par défaut est obligatoire. Le SWITCH minimal ne contiendrait qu'un label (celui par défaut).Mais dans ce cas vaut mieux utiliser un GOTO.

I.6.i.6) Instructions de saut de type sous routine

Il s'agit en fait d'une paire d'instruction, une qui saute (JSR) et l'autre qui retourne juste après l'appel de JSR : RET.
Ce sont des instructions avancées qu'il faut utiliser prudemment. Vous n'en aurez pas besoin pour écrire vos programmes.
Vous pouvez vous concentrez sur d'autres notions (Comme l'appel de méthode, la création d'objet et les tableaux) et revenir ici plus tard.
Ces instructions servent si dans votre méthode vous devez à plusieurs endroits effectués la même série d'instruction et revenir ensuite où vous étiez.
C'est l'idée du GOSUB en Basic. Ça peut être aussi vu comme un appel à une interruption DOS en assembleur. Pour ceux a qui ces références ne parlent pas, voyez comme si à l'intérieur de la méthode il y avait une autre méthode.
Pour essayer d'être plus clair. Quand en Java on fait ceci : public void methodPrincipal() { // ... Before call this.subroutine(); // ... After call } public void subroutine() { // .... } La méthode methodPrincipal voit son exécution arrêté pour exécuter subroutine et son code reprend après l'appel de subroutine, une fois le code de subroutine est terminé.
On peut alors invoqué subroutine une ou plusieurs fois au sein de methodPrincipal et rependre toujours au bon endroit.
L'idée derrière ce couple d'instruction (JSR / RET) est de pouvoir faire la même chose à l'intérieur de la méthode elle même.
JSR est suivit du label où commence la sous routine.
JSR écrit sur la pile l'adresse de retour. Cette adresse peut être lue avec ASTORE pour la sauvegarder dans une variable locale.
ATTENTION A : La sous routine doit se terminer par RET.
RET suivit du nom de la variable locale contenant l'adresse de retour.
RET ne change pas la pile.
Les sous routines exigent qu'on y entre jamais autrement qu'avec JSR et qu'on en sorte autrement qu'avec RET.
Bien entendu des saut peuvent être fait au sein de la sus routine, mais il faut que ceux-ci reste à l'intérieur de la sous routine.
Une exception, une sous routine peut appeler une autre sous routine, car si on a respecter l'adresse de retour on va reprendre à l'intérieur de la sous routine appelante.
Attention tout de même aux appels récursifs, mesurez bien en les conséquences. et prêtez une attention sévère à l'état de la pile.
Les sous routines peuvent être des outils puissants mais demande une rigueur sur les conséquences sur la pile et sur la gestion de l'adresse de retour.
Un petit exemple : class jhelp.example.Printer import java.io.PrintStream field_reference System PrintStream out systemOut method print { LDC "Test 1" ; Push "Test 1" : [] => ["Test 1"(String)] JSR subroutinePrint ; Execute subroutine 'subroutinePrint' : ["Test 1"(String)] => [] LDC "Test 2" ; Push "Test 2" : [] => ["Test 2"(String)] JSR subroutinePrint ; Execute subroutine 'subroutinePrint' : ["Test 2"(String)] => [] LDC "Test 3" ; Push "Test 3" : [] => ["Test 3"(String)] JSR subroutinePrint ; Execute subroutine 'subroutinePrint' : ["Test 3"(String)] => [] RETURN // The subroutine print text on console // Effect on stack : [..., String] => [...] LABEL subroutinePrint VAR Object addressToReturn ASTORE addressToReturn ; Save adress : [..., String, address] => [..., String] GETSTATIC systemOut ; get System.out : [..., String] => [..., String, systemOut(PrintStream)] SWAP ; Exchange : [..., String, systemOut(PrintStream)] => [..., systemOut(PrintStream), String] INVOKEVIRTUAL PrintStream.println(String) ; System.out.println(String) : [..., systemOut(PrintStream), String] => [...] RET addressToReturn ; Return to calling address : [...] => [...] } Sortie sur la console : Test 1 Test 2 Test 3 Rappel l'adresse écrit sur la pile par JSR juste avant le saut. Mais puisque celle-ci sera consommé par la sous routine (Sinon il y a sûrement un problème) d'un point de vu de l'extérieur elle n'existe pas.

I.6.j) Instructions pour invoqué des méthodes

Ici nous ne parlerons que de trois instructions sur les cinq possibles. Les deux autres demanderaient que nous expliquions des notions qui sorte du cadre de ce tutoriel.
De plus il faudrait pouvoir créer des classes abstraites ou interface, ce que ne permet pas pour le moment notre format de fichier.
Si vous voulez en savoir plus sur INVOKEDYNAMIC et INVOKEINTERFACE je vous conseille de regarder la documentation d'Oracle : Chapter 6. The Java Virtual Machine Instruction Set

Pour appeler une méthode non statique d'une autre classe ou de la classe que l'on écrit (Attention, pour la classe en cours cette instruction ne marchera pas pour les méthodes privées)
L'instruction INVOKEVIRTUAL suivit de la description d'appel à la méthode.
Elle attends sur la pile [..., objet_référence, arg0, .., argn-1] si la méthode à n paramètres ou [..., objet_référence] si la méthode n'a pas de paramètre.
Après avoir lu objet_référence et tous les arguments dont elle a besoin, l'instruction exécute la méthode. Si la méthode retourne une valeur celle-ci est écrite sur la pile, sinon rien est écrit.
La description de la méthode est officiellement : le nom complet de la classe où se trouve la méthode, suivit d'un point, suivit du nom de la méthode, suivit de la signature "compacte" de la méthode.
Par exemple pour System.out.println(String), la méthode println(String) est appelé par le champs out de la classe java.lang.System.
Nous avons vu précédemment que GETSTATIC va nous permettre de récupérer l'instance sur out
out est de type java.io.PrintStream, c'est dans cette classe se trouve la méthode println(String).
Donc la référence à la méthode serait normalement : java.io.PrintStream.println(Ljava/lang/String;)V
Voir l'annexe sur les signature de méthode : Signature de méthode
Pour simplifier l'écriture notre système d'import permet d'écrite la référence à la classe sans mettre la package. Nous autorisons également une autre forme de signature de méthode qui permet de tirer avantage des imports.
L'appel peut alors s'écrire (En admettant qu'un import à java.io.PrintStream soit fait : PrintStream.println(String)
La signature s'écrit : (type0, ..., typen-1):typeReturn OU ():typeReturn OU (type0, ..., typen-1) OU ()
La première forme pour les méthodes à n arguments avec type de retour (Non void).
La seconde pour les méthodes sans argument avec type de retour (Non void).
La troisième pour les méthodes à n arguments sans type de retour (void).
La quatrième pour les méthodes sans argument sans type de retour (void).
Un petit "Hello world !" pour illustrer nos propos : class jhelp.example.HelloWorld import java.io.PrintStream field_reference System PrintStream out systemOut ; Reference to System.out method helloWorld { GETSTATIC systemOut ; Get System.out : [] => [out(PrintStream)] LDC "Hello world !" ; Load constant : [out(PrintStream)] => [out(PrintStream), String] INVOKEVIRTUAL PrintStream.println(String) ; Call out.println : [out(PrintStream), String] => [] RETURN ; The end ! : [] => [] } Sortie console : Hello world !
Pour appeler une méthode statique : INVOKESTATIC
L'instruction INVOKESTATIC suivit de la description d'appel à la méthode.
Elle lit sur la pile ses arguments et y écrit sa valeur de retour sur celle-ci (Si elle a une valeur de retour).
La référence à la méthode se fait comme pour l'instruction précédente.
class jhelp.example.Maximum method maximum parameter float first parameter float second return float { FLOAD first ; Push first : [] => [first(float)] FLOAD second ; Push second : [first(float)] => [first(float), second(float)] INVOKESTATIC Math.max(float, float):float ; Invoke Math.max : [first(float), second(float)] => [float] FRETURN ; Return result : [float] => [] }
L'instruction INVOKESPECIAL permet d'appeler des méthodes privées ou publique de la classe en cours, des méthodes de la classe parent (comme super) ou d'appeler le constructeur d'un objet.
Elle fonctionne comme INVOKEVIRTUAL, l'objet de référence sera évidement this.
Si par exemple vous voulez faire l'équivalent de : package jhelp.example; import javax.swing.JComponent; import java.awt.Graphics; public class MyComponent extends JComponent { // ... @Override public void paintComponent(Graphics graphics) { super.paintComponent(graphics); // ... } //....... } On ferait ainsi : class jhelp.example.MyComponent import javax.swing.JComponent import java.awt.Graphics extends JComponent // ....... method paintComponent parameter Graphics graphics { ALOAD this ; Push this : [] => [this] ALOAD graphics ; Push graphics : [this] => [this, Graphics] INVOKESPECIAL JComponent.paintComponent(Graphics) ; super.paintComponent(Graphics) : [this, Graphics] => [] // .... RETURN } //........ Attention à ne pas se tromper, si par erreur on utilisait INVOKEVIRTUAL à la place de INVOKESPECIAL ça serait comme si on avait écrit : package jhelp.example; import javax.swing.JComponent; import java.awt.Graphics; public class MyComponent extends JComponent { // ... @Override public void paintComponent(Graphics graphics) { this.paintComponent(graphics); // ... } //....... } Et donc on aurait un boucle "infinie".

Petit résumé :

I.6.k) Instruction pour la création d'objet

Un objet se construit en deux étapes.
La première on réserve une zone mémoire où sera stocké l'objet et on récupère un référence dessus. Bien garder en tête qu'on a pas encore un objet mais juste une zone de mémoire.
Il faut ensuite initialiser l'objet en choisissant le constructeur que l'on veut invoqué pour que l'objet soit complet.
L'instruction NEW suivit du nom de la classe à créer.
Il crée la zone mémoire et écrit la référence sur la pile.
Pour initialiser l'objet il faut utiliser INVOKESPECIAL. Le nom de la méthode à utiliser sera toujours <init>. < et > font partit du nom. C'est une chaîne de caractère réservé pour les constructeurs. A part son nom elle s'utilise comme toutes les autre méthodes.
class jhelp.example.CreateObject import java.util.ArrayList method collect return ArrayList { VAR ArrayList list ; list declaration NEW ArrayList ; Create reference on Arraylist : [] => [Arraylist] ASTORE list ; Store reference to list : [Arraylist] => [] ALOAD list ; Get reference from list : [] => [Arraylist] INVOKESPECIAL ArrayList.() ; Call the constructor by default : [Arraylist] => [] ALOAD list ; Get reference from list : [] => [Arraylist] LDC "Test" ; Create constant : [Arraylist] => [Arraylist, String] INVOKEVIRTUAL ArrayList.add(Object):boolean ; Call add(Object):boolean : [Arraylist, String] => [boolean] ALOAD list ; Get reference from list : [boolean] => [boolean, ArrayList] ARETURN ; Return object : [boolean, ArrayList] => [] } Remarque : il y a un moyen plus optimiser que de devoir stocker une variable locale. Mais comme nous n'avons pas encore parlé des instructions qui manipulent la pile nous nous interdisons de les utiliser.

I.6.l) Instructions pour les tableaux

Les tableaux sont aussi des objets, si bien que toutes les références sur es tableaux peuvent être utilisés avec les instructions qui utilisent des références sur des objets. Par contre le contraire est faux.

I.6.l.1) Instructions pour la création de tableaux

L'instruction NEWARRAY suivit du type de tableau, permet de créer un tableau avec des éléments de type primitifs (boolean, char, byte, short, int, long, float ou double).
Elle lit sur la pile la taille du tableau et écrit la référence sur le tableau créé. //... BIPUSH 59 ; Push 59 : [...] => [..., 59(int)] NEWARRAY byte ; Create byte[] : [..., 59(int)] => [..., byte[]] //... Pour créer un tableau de byte de taille 59.

L'instruction ANEWARRAY suivit du nom d'une classe, permet de créer un tableau d'objet de la classe demandée.
Elle lit sur la pile la taille du tableau et écrit la référence sur le tableau créé. //... BIPUSH 123 ; Push 123 : [...] => [..., 123(int)] ANEWARRAY String ; Create String[] : [..., 123(int)] => [..., String[]] //.... Pour créer un tableau de java.lang.String de taille 123.

L'instruction MULTIANEWARRAY suivit d'un type et d'un entier, permet la création d'un tableau à plusieurs dimensions.
Le type précise le type des éléments du tableau, tandis que l'entier est le nombre de dimensions. Le nombre de dimensions doit être entre 1 et 127.
L'instruction MULTIANEWARRAY lit sur la pile autant de int qu'il y a de dimensions. Ces entiers sont les tailles du tableau pour chaque dimension.
L'instruction MULTIANEWARRAY écrit sur la pile la référence du tableau créé. //... ICONST 3 ; Push 3 : [...] => [..., 3(int)] ICONST 2 ; Push 2 : [..., 3(int)] => [..., 3(int), 2(int)] MULTIANEWARRAY String 2 ; Create String[3][2] : [..., 3(int), 2(int)] => [..., String[][]] //.. // ... ICONST 1 ; Push 1 : [...] => [..., 1(int)] ICONST 4 ; Push 4 : [..., 1(int)] => [..., 1(int), 4(int)] MULTIANEWARRAY int 2 ; Create int[1][4] : [..., 1(int), 4(int)] => [..., int[][]] // ...

I.6.l.2) Instructions pour lire, écrire dans les tableaux

Les instructions AALOAD, BALOAD, CALOAD, DALOAD, FALOAD, IALOAD, LALOAD et SALOAD permettent de lire un élément d'un tableau.
Elles n'ont pas de paramètres.
Elles lisent sur la pile la référence sur la tableau, ainsi que l'index int de la case du tableau à lire. Elles écrivent la valeur lue sur la pile.
// ... [...] => [..., array(boolean[])] ICONST 2 ; Push 2 : [..., array(boolean[])] => [..., array(boolean[]), 2(int)] BALOAD ; Get array[2] : [..., array(boolean[]), 2(int)] => [..., int] // .... // .... [...] => [..., texts(String[][])] ICONST 1 ; Push 1 : [..., texts(String[][])] => [..., texts(String[][]), 1(int)] AALOAD ; Get texts[1] : [..., texts(String[][]), 1(int)] => [..., String[]] ICONST 3 ; Push 3 : [..., String[]] => [..., String[], 3] AALOAD ; Get texts[1][3] : [..., String[], 3] => [..., String] // ...
Les instructions AASTORE, BASTORE , CASTORE, DASTORE, FASTORE, IASTORE, LASTORE et SASTORE permettent d'écrire un élément sur un tableau.
Elles n'ont pas de paramètres.
Elles lisent sur la pile la référence au tableau, l'index int et la valeur à écrire. Elles n'écrivent rien.
// ... [...] => [..., array(float[])] ICONST 2 ; Push 2 : [..., array(float[])] => [..., array(float[]), 2(int)] FCONST 1 ; Push 1 : [..., array(float[]), 2(int)] => [..., array(float[]), 2(int), 1(float)] FASTORE ; array[2]=1 : [..., array(float[]), 2(int), 1(float)] => [...] // ...

I.6.l.3) Instruction pour avoir la taille d'un tableau

L'instruction ARRAYLENGTH n'a pas de paramètre. Elle permet de connaître la taille d'un tableau.
Elle lit sur la pile la référence sur le tableau. Elle écrit la taille du tableau int.
Pour les tableaux à plusieurs dimensions, elle renverra la taille de la dernière dimension. Pour avoir la taille des autres dimensions il faudra combiner avec AALOAD.
// ... [...] => [..., array(double[])] ARRAYLENGTH ; array.length : [..., array(double[])] => [..., int] // ...

I.6.m) Instructions de manipulation de la pile

Ces instructions permettent soit de dupliquer des parties de la pile, soit d'en échanger, soit d'en supprimer.
Ainsi on évite la création de variable temporaire et ces instructions sont rapides.
Ces instructions n'ont pas de paramètres.
Ces instructions divisent ce qu'elles lisent en deux catégories :
  1. Les long ou les double
  2. Les autres
Ceci est du que les long et les double prennent deux fois plus de place sur la pile que les autres (Les références sur des objets prennet la même place qu'un int). Nous prenons pour convention de nommé les élément de type 1 tous ce qui n'est ni long, ni double.
Nous nommeront de type 2 les long ou les double.
Pour les instructions à plusieurs choix, si aucun des cas n'est satisfait, il y aura un crash.

L'instruction SWAP lit deux éléments de type 1, les échanges, et les écrit à nouveau : // ... : [...] => [..., index(int), name(String)] SWAP ; Exchange : [..., index(int), name(String)] => [..., name(String), index(int)] // ...
L'instruction POP lit une instruction de type 1 et n'écrit rien : // ... [...] => [..., float] POP ; pop : [..., float] => [...] // ... L'instruction POP2 a deux comportement : //... [...] => [..., int, float] POP2 ; pop2 : [..., int, float] => [...] // ... // ... [...] => [..., long] POP2 ; pop2 : [..., long] => [...] // ...
L'instruction DUP va lire un élément de type 1 et l'écrire deux fois : // ... [...] => [..., name(String)] DUP : Duplicate ; [..., name(String)] => [..., name(String), name(String)] // ... L'instruction DUP_X1 va lire deux éléments de type 1 (élément1 et élément2). Elle va écrire : élément2, élément1 puis encore élément2
// ... [...] => [..., age(int), name(String)] DUP_X1 ; Duplicate X1 : [..., age(int), name(String)] => [..., name(String), age(int), name(String)] // ... L'instruction DUP_X2 à deux comportements selon l'état de la pile : // ... [...] => [..., int, float, String] DUP_X2 ; Duplicate X2 : [..., int, float, String] => [..., String, int, float, String] // ... // ... [...] => [..., double, int] DUP_X2 ; Duplicate X2 : [..., double, int] => [..., int, double, int] // ... L'instruction DUP2 à deux comportements selon l'état de la pile : // ... [...] => [..., float, Object] DUP2 ; Duplicate 2 ; [..., float, Object] => [..., float, Object, float, Object] // ... // ... [...] => [..., long] DUP2 ; Duplicate 2 ; [..., long] => [..., long, long] // ... L'instruction DUP2_X1 à deux comportements selon l'état de la pile : // ... [...] => [..., int, float, JPanel] DUP2_X1 ; duplicate 2 X1 : [..., int, float, JPanel] => [..., float, JPanel, int, float, JPanel] // ... // ... [...] => [..., Object, double] DUP2_X1 ; duplicate 2 X1 : [..., Object, double] => [..., double, Object, double] // ... L'instruction DUP2_X2 à quatre comportements selon l'état de la pile : // ... [...] => [..., int, Object, float, String] // DUP2_X2 ; duplicate 2 X2 : [..., int, Object, float, String] => [..., float, String, int, Object, float, String] // ... // ... [...] => [..., int, float, long] // DUP2_X2 ; duplicate 2 X2 : [..., int, float, long] => [..., long, int, float, long] // ... // ... [...] => [..., double, Object, String] // DUP2_X2 ; duplicate 2 X2 : [..., double, Object, String] => [..., Object, String, double, Object, String] // ... // ... [...] => [..., long, double] // DUP2_X2 ; duplicate 2 X2 : [..., long, double] => [..., double, long, double] // ...
Afin d'appliquer quelques unes de c'est instructions dans un cas concret, reprenons l'exemple qu'on a écrit plus haut : class jhelp.example.CreateObject import java.util.ArrayList method collect return ArrayList { VAR ArrayList list ; list declaration NEW ArrayList ; Create reference on Arraylist : [] => [Arraylist] ASTORE list ; Store reference to list : [Arraylist] => [] ALOAD list ; Get reference from list : [] => [Arraylist] INVOKESPECIAL ArrayList.() ; Call the constructor by default : [Arraylist] => [] ALOAD list ; Get reference from list : [] => [Arraylist] LDC "Test" ; Create constant : [Arraylist] => [Arraylist, String] INVOKEVIRTUAL ArrayList.add(Object):boolean ; Call add(Object):boolean : [Arraylist, String] => [boolean] ALOAD list ; Get reference from list : [boolean] => [boolean, ArrayList] ARETURN ; Return object : [boolean, ArrayList] => [] } Et maintenant nous allons le réécrire en utilisant des instructions de manipulation de pile afin que l'utilisation de la variable locale ne soit plus nécessaire.
Pour cela découpons ce qu'on veut faire en étape.
  1. Créer une instance de java.util.ArrayList à l'aide du constructeur par défaut
  2. Ajouter "Test" dans l'instance de java.util.ArrayList
  3. Retourner l'instance de java.util.ArrayList
Étape 1 : NEW ArrayList ; Create reference on Arraylist : [...] => [..., Arraylist] INVOKESPECIAL ArrayList.() ; Call the constructor by default : [..., Arraylist] => [...] Si elle es prise dans son ensemble elle ne modifie pas la pile

Étape 2 : LDC "Test" ; Create constant : [...] => [..., String] INVOKEVIRTUAL ArrayList.add(Object):boolean ; Call add(Object):boolean : [..., String] => CRASH ! Il nous manque la référence à l'instance de java.util.ArrayList !
On doit donc avoir l'instance dans la pile : LDC "Test" ; Create constant : [..., ArrayList] => [..., ArrayList, String] INVOKEVIRTUAL ArrayList.add(Object):boolean ; Call add(Object):boolean : [..., ArrayList, String] => [boolean] Pour cela il faut s'arranger que l'étape 1 laisse sur la pile l'instance dont on a besoin.
Bien sûr il faut que l'instance existe, donc si on veut faire quelque chose c'est après l'instruction NEW.
Comme l'instruction INVOKESPECIAL va consommer cette référence il faut le faire avant elle.
Il ne nous reste qu'a insérer une instruction entre les deux lignes de l'étape.
On veut que sur la pile se trouve deux fois l'instance ainsi quand INVOKESPECIAL va en consommer une il nous en restera toujours une pour l'étape 2.
Il s'agit donc de dupliquer une référence sur un objet, c'est à dire un élément de type 1, on utilsera donc DUP.
L'étape 1 devient: NEW ArrayList ; Create reference on Arraylist : [...] => [..., Arraylist] DUP ; duplicate : [..., Arraylist] => [..., Arraylist, ArrayList] INVOKESPECIAL ArrayList.() ; Call the constructor by default : [Arraylist, Arraylist] => [..., Arraylist] Les étapes 1 et 2 cumulées on pour effet sur la pile : [...] => [boolean]
Étape 3 : ARETURN ; Return object : [..., ArrayList] => [] Elle attends l'instance de java.util.ArrayList. Pour ce faire on ajoute un second DUP juste après le premier.
Les étapes 1 et 2 cumulées on désormais pour effet sur la pile : [...] => [ArrayList, boolean] (On vous laisse vérifier par vous même)
On se moque du boolean, on veut l'instance de java.util.ArrayList. Un POP avant le ARETURN va régler le problème.
Le code devient: class jhelp.example.CreateObject import java.util.ArrayList method collect return ArrayList { NEW ArrayList ; Create reference on Arraylist : [] => [Arraylist] DUP ; Duplicate : [Arraylist] => [Arraylist, Arraylist] DUP ; Duplicate : [Arraylist, Arraylist] => [Arraylist, Arraylist, ArrayList] INVOKESPECIAL ArrayList.() ; Call the constructor by default : [Arraylist, Arraylist, ArrayList] => [Arraylist, Arraylist] LDC "Test" ; Create constant : [Arraylist, Arraylist] => [Arraylist, Arraylist, String] INVOKEVIRTUAL ArrayList.add(Object):boolean ; Call add(Object):boolean : [Arraylist, Arraylist, String] => [Arraylist, boolean] POP . Pop : [Arraylist, boolean] => [ArrayList] ARETURN ; Return object : [ArrayList] => [] } Remarque : Le second DUP aurait aussi bien pu se placer juste avant LDC.

I.6.n) Instructions pour "casté des objets

L'instruction INSTANCEOF suivit d'un nom de classe. Permet de tester si une référence sur un objet peut être vu comme une instance de la classe donnée.
Elle lit sur la pile l'instance de l'objet à tester, elle écrit un 0 ou 1 int.
Le 0 signifiant que l'objet ne peut pas être vu comme une instance de la classe donnée.
Le 1 signifiant que l'objet peut être vu comme une instance de la classe donnée.

L'instruction CHECKCAST suivit d'un nom de classe. Permet de "casté" un objet dans une autre classe.
Elle lit sur la pile l'instance de l'objet à transformer. Elle écrit l'objet transformé si le "cast" a réussit. Elle crash si le "cast" n'est pas possible.
// ... [...] => [..., Object] DUP ; duplicate : [..., Object] => [..., Object, Object] INSTANCEOF String ; Is String ? : [..., Object, Object] => [..., Object, int] IFEQ notString ; IF == 0 jump : [..., Object, int] => [..., Object] CHECKCAST String ; Cast to String : [..., Object] => [..., String] // ...

I.6.o) Instruction pour les exceptions

Pour le moment pas de try...catch possible par notre format.Les exceptions sont donc pour toute la méthode.
Ne pas oublier que la méthode doit déclarée qu'elle peut lever une exception avec throws.
L'instructions ATHROW n'a pas de paramètre. Elle lève une exception.
Elle lit sur la pile l'instance de l'exception à levée. Elle l'a récrit. La méthode arrête son exécution et sort. La réécriture sert aux classe appelantes pour récupérer l'exception levée.
// ... method checkNull parameter Object object throws NullPointerException { ALOAD object ; load object : [] => [Object] IFNONNULL exit ; IF != null JUMP : [Object] => [] ; Reach if object==null : NEW NullPointerException ; Create NullPointerException : [] => [NullPointerException] DUP ; Duplicate : [NullPointerException] => [NullPointerException, NullPointerException] LDC "object is null !" ; Load constant : [NullPointerException, NullPointerException] => [NullPointerException, NullPointerException, String] INVOKESPECIAL NullPointerException.(String) ; Call constructor : [NullPointerException, NullPointerException, String] => [NullPointerException] ATHROW ; Throw exception : [NullPointerException] => [NullPointerException] LABEL exit ; Reach if object != null : RETURN ; Good bye ! : [] => [] } //...

I.7) Glossaire

Pour finir un petit résumé sur le chapitre.

Nous prendrons pour convention :
Les mots clefs de construction d'une classe:
Mot clefSyntaxInformationsVoir
classclass <nomClasseComplet>Déclare le nom de la classeI.5) Format du fichier
importimport <nomClasseComplet>Importe une classeI.5) Format du fichier
extendsextends <nomClasse>Déclare la classe parentI.5) Format du fichier
implementsimplements <nomClasse>Implémente une interfaceI.5) Format du fichier
fieldfield <type> <nom>Déclare un champsI.5) Format du fichier
field_referencefield_reference <nomClasse> <type> <nom> <alias>Référence à un champs d'une autre classeI.5) Format du fichier
methodmethod <nom>Déclare une méthodeI.5) Format du fichier
parameterparameter <type> <nom>Ajoute un paramètre à la méthodeI.5) Format du fichier
throwsthrows <nomClasse>Ajoute une déclaration que la méthode peut levée une exceptionI.5) Format du fichier
returnretrun <type>Indique le type de retour d'une méthodeI.5) Format du fichier
{{Indique le début du code d'une méthodeI.5) Format du fichier
}}Indique la fin du code d'une méthodeI.5) Format du fichier

Les instructions de code :
InstructionEffet sur la pileInformationsVoir
LABEL <label> ... => ... Déclare un lieu de sautI.6.i) Instructions pour "sauter" dans la méthode
VAR <type> <nom> ... => ... Déclare une variable localeI.6.a) Instruction de déclaration de variable locale
AALOAD ..., <array>, int => ..., <object> Lit un élément d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
AASTORE ..., <array> int, <object> => ... Écrit un élément dans un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
ACONST_NULL ... => null Pousse null sur la pileI.6.g) Instructions pour pousser des constantes
ALOAD <nom> ... => <object> Lit une variable localeI.6.b) Instructions de chargement de valeur
ANEWARRAY <nomClasse> ..., int => ..., <array> Créé un tableauI.6.l.1) Instructions pour la création de tableaux
ARETURN ..., <object> => [] Retourne une valeurI.6.d) Instructions de retour
ARRAYLENGTH ..., <array> => ..., int Taille d'un tableauI.6.l.3) Instruction pour avoir la taille d'un tableau
ASTORE <nom> ..., <object> => ... Écrit sur une variable localeI.6.c) Instructions de sauvegarde de valeur
ATHROW ..., <object> => <object> Lève un exceptionI.6.o) Instruction pour les exceptions
BALOAD ..., byte/boolean[], int => ..., int Lit un élément d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
BASTORE ..., byte/boolean[], int, int => ... Écrit sur l'élément d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
BIPUSH int ... => ..., int Pousse un entier entre -128 et 127I.6.g) Instructions pour pousser des constantes
CALOAD ..., char[], int => ..., int Lit un élément d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
CASTORE ..., char[], int, int => ... Écrit un élément sur un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
CHECKCAST <typeStrict> ..., <object> => ..., <object> Caste un objetI.6.n) Instructions pour "casté" des objets
D2F ..., double => ..., float Convertit un double en floatI.6.f) Instructions pour convertir
D2I ..., double => ..., int Convertit un double en intI.6.f) Instructions pour convertir
D2L ..., double => ..., long Convertit un double en longI.6.f) Instructions pour convertir
DADD ..., double, double => ..., double Additionne deux nombresI.6.e) Instructions de calcul
DALOAD ..., double[], int => ..., double Lit un élément d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
DASTORE ..., double[], int, double => ... Écrit un élément dans un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
DCMPG ..., double, double => ..., int Compare deux nombresI.6.i.4) Instructions de comparaison
DCMPL ..., double, double => ..., int Compare deux nombresI.6.i.4) Instructions de comparaison
DCONST double ... => ..., double Pousse 0 ou 1I.6.g) Instructions pour pousser des constantes
DDIV ..., double, double => ..., double Divise deux nombresI.6.e) Instructions de calcul
DLOAD <nom> ... => ..., double Lit une variable localeI.6.b) Instructions de chargement de valeur
DMUL ..., double, double => ..., double Multiplie deux nombresI.6.e) Instructions de calcul
DNEG ..., double => ..., double Prend l'opposé d'un nombreI.6.e) Instructions de calcul
DREM ..., double, double => ..., double Calcul le reste de la division entre deux nombresI.6.e) Instructions de calcul
DRETURN ..., double => [] Retourne un nombreI.6.d) Instructions de retour
DSTORE <nom> ..., double => ... Écrit dans une variable localeI.6.c) Instructions de sauvegarde de valeur
DSUB ..., double, double => ..., double Soustrait deux nombresI.6.e) Instructions de calcul
DUP ..., <T1> => ..., <T1>, <T1> Duplique une valeurI.6.m) Instructions de manipulation de la pile
DUP_X1 ..., <T1>1, <T1>2 => ..., <T1>2, <T1>1, <T1>2 Duplique une valeurI.6.m) Instructions de manipulation de la pile
DUP_X2 ..., <T1>1, <T1>2, <T1>3 => ..., <T1>3, <T1>1, <T1>2, <T1>3
OU

..., <T2>1, <T1>2 => ..., <T1>2, <T2>1, <T1>2
Duplique une valeurI.6.m) Instructions de manipulation de la pile
DUP2 ..., <T1>1, <T1>2 => ..., <T1>1, <T1>2, <T1>1, <T1>2
OU

..., <T2> => ..., <T2>, <T2>
Duplique une ou deux valeur(s)I.6.m) Instructions de manipulation de la pile
DUP2_X1 ..., <T1>1, <T1>2, <T1>3 => ..., <T1>2, <T1>3, <T1>1, <T1>2, <T1>3
OU

..., <T1>1, <T2>2 => ..., <T2>2, <T1>1, <T2>2
Duplique une ou deux valeur(s)I.6.m) Instructions de manipulation de la pile
DUP2_X2 ..., <T1>1, <T1>2, <T1>3, <T1>4 => ..., <T1>3, <T1>4, <T1>1, <T1>2, <T1>3, <T1>4
OU

..., <T1>1, <T1>2, <T2>3 => ..., <T2>3, <T1>1, <T1>2, <T2>3
OU

..., <T2>1, <T1>2, <T1>3 => ..., <T1>2, <T1>3, <T2>1, <T1>2, <T1>3
OU

..., <T2>1, <T2>2 => ..., <T2>2, <T2>1, <T2>2
Duplique une ou deux valeur(s)I.6.m) Instructions de manipulation de la pile
F2D ..., float => ... double Convertit un float en doubleI.6.f) Instructions pour convertir
F2I ..., float => ... int Convertit un float en intI.6.f) Instructions pour convertir
F2L ..., float => ... long Convertit un float en longI.6.f) Instructions pour convertir
FADD ..., float, float => ..., float Additionne deux nombresI.6.e) Instructions de calcul
FALOAD ..., float[], int => ..., float Lit un élément de tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
FASTORE ..., float[], int, float => ... Écrit un élément dans un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
FCMPG ..., float, float => ..., int Compare deux nombresI.6.i.4) Instructions de comparaison
FCMPL ..., float, float => ..., int Compare deux nombresI.6.i.4) Instructions de comparaison
FCONST float ... => ..., float Pousse 0, 1 ou 2I.6.g) Instructions pour pousser des constantes
FDIV ..., float, float => ..., float Divise deux nombresI.6.e) Instructions de calcul
FLOAD <nom> ... => ..., float Lit une variable localeI.6.b) Instructions de chargement de valeur
FMUL ..., float, float => ..., float Multiplie deux nombresI.6.e) Instructions de calcul
FNEG ..., float => ..., float Prend l'opposé d'un nombreI.6.e) Instructions de calcul
FREM ..., float, float => ..., float Calcul le reste de la division entre deux nombresI.6.e) Instructions de calcul
FRETURN ..., float => [] Retourne un nombreI.6.d) Instructions de retour
FSTORE <nom> ..., float => ... Écrit dans une variable localeI.6.c) Instructions de sauvegarde de valeur
FSUB ..., float, float => ..., floatSoustrait deux nombresI.6.e) Instructions de calcul
GETFIELD <nom/alias> ..., <object> => ..., <valeur> Lit un champsI.6.b) Instructions de chargement de valeur
GETSTATIC <nom/alias> ... => ..., <valeur> Lit un champsI.6.b) Instructions de chargement de valeur
GOTO <label> ... => ... Saute au label indiquéI.6.i.1) Instruction de saut quoi qu'il arrive
I2B ..., int => ..., int Tronque un int en byteI.6.f) Instructions pour convertir
I2C ..., int => ..., int Tronque un int en charI.6.f) Instructions pour convertir
I2D ..., int => ..., doubleConvertit un int en doubleI.6.f) Instructions pour convertir
I2F ..., int => ..., floatConvertit un int en floatI.6.f) Instructions pour convertir
I2L ..., int => ..., longConvertit un int en longI.6.f) Instructions pour convertir
I2S ..., int => ..., int Tronque un int en shortI.6.f) Instructions pour convertir
IADD ..., int, int => ..., int Additionne deux nombresI.6.e) Instructions de calcul
IALOAD ..., int[], int => ..., int Lit un élément dans un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
IAND ..., int, int => ..., int Et logique entre entiersI.6.e) Instructions de calcul
IASTORE ..., int[], int, int => ... Écrit dans un élément d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
ICONST int ... => ..., int Pousse -1, 0, 1, 2, 3, 4 ou 5I.6.g) Instructions pour pousser des constantes
IDIV ..., int, int => ..., int Divise deux nombresI.6.e) Instructions de calcul
IF_ACMPEQ <label> ..., <object>, <object> => ... Saute si == I.6.i.3) Instructions conditionnelles comparant deux valeurs
IF_ACMPNE <label> ..., <object>, <object> => ... Saute si != I.6.i.3) Instructions conditionnelles comparant deux valeurs
IF_ICMPEQ <label> ..., int, int => ... Saute si == I.6.i.3) Instructions conditionnelles comparant deux valeurs
IF_ICMPGE <label> ..., int, int => ... Saute si >= I.6.i.3) Instructions conditionnelles comparant deux valeurs
IF_ICMPGT <label> ..., int, int => ... Saute si > I.6.i.3) Instructions conditionnelles comparant deux valeurs
IF_ICMPLE <label> ..., int, int => ... Saute si <= I.6.i.3) Instructions conditionnelles comparant deux valeurs
IF_ICMPLT <label> ..., int, int => ... Saute si < I.6.i.3) Instructions conditionnelles comparant deux valeurs
IF_ICMPNE <label> ..., int, int => ... Saute si != I.6.i.3) Instructions conditionnelles comparant deux valeurs
IFEQ <label> ..., int => ... Saut si == 0I.6.i.2) Instructions conditionnelles comparant à zéro ou null
IFGE <label> ..., int => ... Saut si >= 0I.6.i.2) Instructions conditionnelles comparant à zéro ou null
IFGT <label> ..., int => ... Saut si > 0I.6.i.2) Instructions conditionnelles comparant à zéro ou null
IFLE <label> ..., int => ... Saut si <= 0I.6.i.2) Instructions conditionnelles comparant à zéro ou null
IFLT <label> ..., int => ... Saut si < 0I.6.i.2) Instructions conditionnelles comparant à zéro ou null
IFNE <label> ..., int => ... Saut si != 0I.6.i.2) Instructions conditionnelles comparant à zéro ou null
IFNONNULL <label> ..., object => ... Saut si != nullI.6.i.2) Instructions conditionnelles comparant à zéro ou null
IFNULL <label> ..., object => ... Saut si == nullI.6.i.2) Instructions conditionnelles comparant à zéro ou null
IINC <nom> int ... => ... Incrémente une variable locale d'un entier entre -128 et 127I.6.h) Instruction pour incrémenter
ILOAD <nom> ... => ..., int Lit une variable localeI.6.b) Instructions de chargement de valeur
IMUL ..., int, int => ..., int Multiplie deux nombresI.6.e) Instructions de calcul
INEG ..., int => ..., int Prend l'opposé d'un nombreI.6.e) Instructions de calcul
INSTANCEOF <typeStrict> ..., <object> => ..., int Vérifie le type d'un objetI.6.n) Instructions pour "caster" des objets
INVOKESPECIAL <signatureMéthode> ..., <object>, (<valeur>)* => ...
OU

..., <object>, (<valeur>)* => ..., <valeur>
Invoque une méthode super, constructeur (<init>), ou privéeI.6.j) Instructions pour invoqué des méthodes
INVOKESTATIC <signatureMéthode> ..., (<valeur>)* => ...
OU

..., (<valeur>)* => ..., <valeur>
Invoque une méthode statiqueI.6.j) Instructions pour invoqué des méthodes
INVOKEVIRTUAL <signatureMéthode> ..., <object>, (<valeur>)* => ...
OU

..., <object>, (<valeur>)* => ..., <valeur>
Invoque une méthode non statiqueI.6.j) Instructions pour invoqué des méthodes
IOR ..., int, int => ..., int Ou logique sur des entiersI.6.e) Instructions de calcul
IREM ..., int, int => ..., int Reste de la divisionI.6.e) Instructions de calcul
IRETURN ..., int => [] Retourne un entierI.6.d) Instructions de retour
ISHL ..., int, int => ..., int Fait un décalage binaire vers la gaucheI.6.e) Instructions de calcul
ISHR ..., int, int => ..., int Fait un décalage binaire signé vers la droiteI.6.e) Instructions de calcul
ISTORE <nom> ..., int => ... Écrit un entier sur une variable locale
ISUB ..., int, int => ..., int Soustrait deux entiersI.6.e) Instructions de calcul
IUSHR ..., int, int => ..., int Fait un décalage binaire non signé vers la droiteI.6.e) Instructions de calcul
IXOR ..., int, int => ..., int Ou exclusif logique sur des entiersI.6.e) Instructions de calcul
JSR <label> ... => ..., adresse Saut à une sous routineI.6.i.6) Instructions de saut de type sous routine
L2D ..., long => ..., double Convertit un long en doubleI.6.f) Instructions pour convertir
L2I ..., long => ..., int Convertit un long en intI.6.f) Instructions pour convertir
L2F ..., long => ..., float Convertit un long en floatI.6.f) Instructions pour convertir
LADD ..., long, long => ..., long Additionne deux entiersI.6.e) Instructions de calcul
LALOAD ..., long[], int => ..., long Lit en entier d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
LAND ..., long, long => ..., long Et logique entre deux entiersI.6.e) Instructions de calcul
LASTORE ..., long[], int, long => ... Écrit un entier dans un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
LCMP ...long, long => ..., int Compare deux entiersI.6.i.4) Instructions de comparaison
LCONST int ... => ..., long Pousse 0 ou 1I.6.g) Instructions pour pousser des constantes
LDC <T1> ... => ..., <T1> Pousse une constanteI.6.g) Instructions pour pousser des constantes
LDC_W <T1> ... => ..., <T1> Pousse une constanteI.6.g) Instructions pour pousser des constantes
LDC2_W <T2> ... => ..., <T2> Pousse une constanteI.6.g) Instructions pour pousser des constantes
LDIV ..., long, long => ..., long Divise deux entiersI.6.e) Instructions de calcul
LLOAD <nom> ... => ..., long Lit une variable localeI.6.b) Instructions de chargement de valeur
LMUL ..., long, long => ..., long Multiplie deux entiersI.6.e) Instructions de calcul
LNEG ..., long => ..., long Prend l'opposé d'un entierI.6.e) Instructions de calcul
LOOKUPSWITCH (int <label>)* <label> ..., int => ... Saut selon une valeurI.6.i.5) Instructions de saut switch
LOR ..., long, long => ..., long Ou logique entre deux entiersI.6.e) Instructions de calcul
LREM ..., long, long => ..., long Reste de la division entre deux entiersI.6.e) Instructions de calcul
LRETURN ..., long => [] Retourne un entierI.6.d) Instructions de retour
LSHL ..., long, int => ..., long Décalage binaire vers la gaucheI.6.e) Instructions de calcul
LSHR ..., long, int => ..., long Décalage binaire signé vers la droiteI.6.e) Instructions de calcul
LSTORE <nom> ..., long => ... Écrit un entier sur une variable localeI.6.c) Instructions de sauvegarde de valeur
LSUB ..., long, long => ..., long Soustrait deux entiersI.6.e) Instructions de calcul
LUSHR ..., long, int => ..., long Décalage binaire non signé vers la droiteI.6.e) Instructions de calcul
LXOR ..., long, long => ..., long Ou exclusif logique entre deux entiersI.6.e) Instructions de calcul
MULTIANEWARRAY <type> int ..., (int)+ => ..., <array> Créer un tableau à plusieurs dimensionsI.6.l.1) Instructions pour la création de tableaux
NEW< <nomClasse> ... => ..., <object> Crée une nouvelle référenceI.6.k) Instruction pour la création d'objet
NEWARRAY <primitif> ..., int => <array> Crée un nouveau tableauI.6.l.1) Instructions pour la création de tableaux
POP ..., <T1> => ... Consomme un élément de la pileI.6.m) Instructions de manipulation de la pile
POP2 ..., <T2> => ... Consomme un élément de la pileI.6.m) Instructions de manipulation de la pile
PUSH <valeur> ... => ..., <valeur> Pousse une constanteI.6.g) Instructions pour pousser des constantes
PUTFIELD <nom/alias> ..., <object>, <valeur> => ... Écrit un valeur dans un champs non statiqueI.6.c) Instructions de sauvegarde de valeur
PUTSATIC <nom/alias> ..., <valeur> => ... Écrit un valeur dans un champs statiqueI.6.c) Instructions de sauvegarde de valeur
RET <nom> ... => ... Retourne d'une sous routine (Fin d'une sous routine)I.6.i.6) Instructions de saut de type sous routine
RETURN ... => [] Sort d'une méthode sans retourI.6.d) Instructions de retour
SALOAD ..., short[], int => ..., int Lit un élément d'un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
SASTORE ..., short[], int, int => ... Écrit un élément dans un tableauI.6.l.2) Instructions pour lire, écrire dans les tableaux
SIPUSH int ... => int Pousse un entier entre -32768 et 32767I.6.g) Instructions pour pousser des constantes
SWAP ..., <T1>1, <T1>2 => ..., , <T1>2, <T1>1 Échange deux éléments de la pileI.6.m) Instructions de manipulation de la pile
SWITCH (int <label>)* <label> ..., int => ... Saut selon une valeurI.6.i.5) Instructions de saut switch
TABLESWITCH (int <label>)* <label> ..., int => ... Saut selon une valeurI.6.i.5) Instructions de saut switch