method minimal
{
RETURN
}
...
// 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ù.class jhelp.asm.Test
Pour créer la classe Test dans le package jhelp.asmimport java.util.List
Vous remarquerez au passage que le ; n'est pas obligatoire, car il n'y a qu'une instruction par ligne.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 implements Comparable
implements Iterable
Bien entendu votre classe devra ensuite respecter les contrats de la classe parent et des interfaces implémentées.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
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.VAR double salary
VAR String name
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.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.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
}
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.method test
//...
return short
{
// .....
// .... [...] => [..., int]
IRETURN
}
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)] = > []
}
Expression | Binaire | Décimal |
---|---|---|
number | 11111111111111111111111111001001 | -55 |
shift | 00000000000000000000000000000011 | 3 |
ISHR number shift | 11111111111111111111111111111001 | -7 |
IUSHR number shift | 00011111111111111111111111111001 | 536870905 |
ISHL number shift | 11111111111111111111111001001000 | -440 |
IAND number shift | 00000000000000000000000000000001 | 1 |
IOR number shift | 11111111111111111111111111001011 | -53 |
IXOR number shift | 11111111111111111111111111001010 | -54 |
Expression | Type poussé sur la pile | Information |
---|---|---|
true | int | 1 est poussé |
false | int | 0 est poussé |
'a' | int | Le code ASCII de du caractère 'a' est poussé. Comme en Java le caractère doit être entre simple guillemet. |
123 | int | 123 est poussé |
12.34f | float | 12.34 est poussé. Il faut ajouté à la fin du nombre un F minuscule ou majuscule pour précisé qu'on veut poussé un float |
1234L | long | 1234 est poussé. Il faut ajouté à la fin du nombre un L minuscule ou majuscule pour précisé qu'on veut poussé un long |
12.34 | double | 12.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.String | Enregistre 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 |
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 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.// ...
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)] => [...]
// .....
Instruction | Condition de saut |
---|---|
IFEQ | Si le int lu sur la pile est égal à zéro |
IFGE | Si le int lu sur la pile est plus grand ou égal à zéro |
IFGT | Si le int lu sur la pile est strictement plus grand que zéro |
IFLE | Si le int lu sur la pile est plus petit ou égal à zéro |
IFLT | Si le int lu sur la pile est strictement plus petit que zéro |
IFNE | Si 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)] => []
}
Instruction | Condition de saut |
---|---|
IF_ICMPEQ | first == second |
IF_ICMPGE | first >= second |
IF_ICMPGT | first > second |
IF_ICMPLE | first <= second |
IF_ICMPLT | first < second |
IF_ICMPNE | first != 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)] => []
}
Condition | Valeur int écrite |
---|---|
first < second | -1 |
first == second | 0 |
first > second | 1 |
//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)] => []
}
// ...... : [...] => [..., 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é :
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é.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.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 !
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] => []
}
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".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.//...
BIPUSH 59 ; Push 59 : [...] => [..., 59(int)]
NEWARRAY byte ; Create byte[] : [..., 59(int)] => [..., byte[]]
//...
Pour créer un tableau de byte de taille 59.//...
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.//...
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[][]]
// ...
// ... [...] => [..., 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]
// ...
// ... [...] => [..., 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)] => [...]
// ...
// ... [...] => [..., array(double[])]
ARRAYLENGTH ; array.length : [..., array(double[])] => [..., int]
// ...
// ... : [...] => [..., index(int), name(String)]
SWAP ; Exchange : [..., index(int), name(String)] => [..., name(String), index(int)]
// ...
// ... [...] => [..., float]
POP ; pop : [..., float] => [...]
// ...
L'instruction POP2 a deux comportement :
//... [...] => [..., int, float]
POP2 ; pop2 : [..., int, float] => [...]
// ...
// ... [...] => [..., long]
POP2 ; pop2 : [..., long] => [...]
// ...
// ... [...] => [..., 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]
// ...
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.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 pileLDC "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 !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.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]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.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.// ... [...] => [..., 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]
// ...
// ...
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 ! : [] => []
}
//...
Mot clef | Syntax | Informations | Voir |
---|---|---|---|
class | class <nomClasseComplet> | Déclare le nom de la classe | I.5) Format du fichier |
import | import <nomClasseComplet> | Importe une classe | I.5) Format du fichier |
extends | extends <nomClasse> | Déclare la classe parent | I.5) Format du fichier |
implements | implements <nomClasse> | Implémente une interface | I.5) Format du fichier |
field | field <type> <nom> | Déclare un champs | I.5) Format du fichier |
field_reference | field_reference <nomClasse> <type> <nom> <alias> | Référence à un champs d'une autre classe | I.5) Format du fichier |
method | method <nom> | Déclare une méthode | I.5) Format du fichier |
parameter | parameter <type> <nom> | Ajoute un paramètre à la méthode | I.5) Format du fichier |
throws | throws <nomClasse> | Ajoute une déclaration que la méthode peut levée une exception | I.5) Format du fichier |
return | retrun <type> | Indique le type de retour d'une méthode | I.5) Format du fichier |
{ | { | Indique le début du code d'une méthode | I.5) Format du fichier |
} | } | Indique la fin du code d'une méthode | I.5) Format du fichier |
Instruction | Effet sur la pile | Informations | Voir |
---|---|---|---|
LABEL <label> | ... => ... | Déclare un lieu de saut | I.6.i) Instructions pour "sauter" dans la méthode |
VAR <type> <nom> | ... => ... | Déclare une variable locale | I.6.a) Instruction de déclaration de variable locale |
AALOAD | ..., <array>, int => ..., <object> | Lit un élément d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
AASTORE | ..., <array> int, <object> => ... | Écrit un élément dans un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
ACONST_NULL | ... => null | Pousse null sur la pile | I.6.g) Instructions pour pousser des constantes |
ALOAD <nom> | ... => <object> | Lit une variable locale | I.6.b) Instructions de chargement de valeur |
ANEWARRAY <nomClasse> | ..., int => ..., <array> | Créé un tableau | I.6.l.1) Instructions pour la création de tableaux |
ARETURN | ..., <object> => [] | Retourne une valeur | I.6.d) Instructions de retour |
ARRAYLENGTH | ..., <array> => ..., int | Taille d'un tableau | I.6.l.3) Instruction pour avoir la taille d'un tableau |
ASTORE <nom> | ..., <object> => ... | Écrit sur une variable locale | I.6.c) Instructions de sauvegarde de valeur |
ATHROW | ..., <object> => <object> | Lève un exception | I.6.o) Instruction pour les exceptions |
BALOAD | ..., byte/boolean[], int => ..., int | Lit un élément d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
BASTORE | ..., byte/boolean[], int, int => ... | Écrit sur l'élément d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
BIPUSH int | ... => ..., int | Pousse un entier entre -128 et 127 | I.6.g) Instructions pour pousser des constantes |
CALOAD | ..., char[], int => ..., int | Lit un élément d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
CASTORE | ..., char[], int, int => ... | Écrit un élément sur un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
CHECKCAST <typeStrict> | ..., <object> => ..., <object> | Caste un objet | I.6.n) Instructions pour "casté" des objets |
D2F | ..., double => ..., float | Convertit un double en float | I.6.f) Instructions pour convertir |
D2I | ..., double => ..., int | Convertit un double en int | I.6.f) Instructions pour convertir |
D2L | ..., double => ..., long | Convertit un double en long | I.6.f) Instructions pour convertir |
DADD | ..., double, double => ..., double | Additionne deux nombres | I.6.e) Instructions de calcul |
DALOAD | ..., double[], int => ..., double | Lit un élément d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
DASTORE | ..., double[], int, double => ... | Écrit un élément dans un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
DCMPG | ..., double, double => ..., int | Compare deux nombres | I.6.i.4) Instructions de comparaison |
DCMPL | ..., double, double => ..., int | Compare deux nombres | I.6.i.4) Instructions de comparaison |
DCONST double | ... => ..., double | Pousse 0 ou 1 | I.6.g) Instructions pour pousser des constantes |
DDIV | ..., double, double => ..., double | Divise deux nombres | I.6.e) Instructions de calcul |
DLOAD <nom> | ... => ..., double | Lit une variable locale | I.6.b) Instructions de chargement de valeur |
DMUL | ..., double, double => ..., double | Multiplie deux nombres | I.6.e) Instructions de calcul |
DNEG | ..., double => ..., double | Prend l'opposé d'un nombre | I.6.e) Instructions de calcul |
DREM | ..., double, double => ..., double | Calcul le reste de la division entre deux nombres | I.6.e) Instructions de calcul |
DRETURN | ..., double => [] | Retourne un nombre | I.6.d) Instructions de retour |
DSTORE <nom> | ..., double => ... | Écrit dans une variable locale | I.6.c) Instructions de sauvegarde de valeur |
DSUB | ..., double, double => ..., double | Soustrait deux nombres | I.6.e) Instructions de calcul |
DUP | ..., <T1> => ..., <T1>, <T1> | Duplique une valeur | I.6.m) Instructions de manipulation de la pile |
DUP_X1 | ..., <T1>1, <T1>2 => ..., <T1>2, <T1>1, <T1>2 | Duplique une valeur | I.6.m) Instructions de manipulation de la pile |
DUP_X2 | ..., <T1>1, <T1>2, <T1>3 => ..., <T1>3, <T1>1, <T1>2, <T1>3 ..., <T2>1, <T1>2 => ..., <T1>2, <T2>1, <T1>2 | Duplique une valeur | I.6.m) Instructions de manipulation de la pile |
DUP2 | ..., <T1>1, <T1>2 => ..., <T1>1, <T1>2, <T1>1, <T1>2 ..., <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 ..., <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 ..., <T1>1, <T1>2, <T2>3 => ..., <T2>3, <T1>1, <T1>2, <T2>3 ..., <T2>1, <T1>2, <T1>3 => ..., <T1>2, <T1>3, <T2>1, <T1>2, <T1>3 ..., <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 double | I.6.f) Instructions pour convertir |
F2I | ..., float => ... int | Convertit un float en int | I.6.f) Instructions pour convertir |
F2L | ..., float => ... long | Convertit un float en long | I.6.f) Instructions pour convertir |
FADD | ..., float, float => ..., float | Additionne deux nombres | I.6.e) Instructions de calcul |
FALOAD | ..., float[], int => ..., float | Lit un élément de tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
FASTORE | ..., float[], int, float => ... | Écrit un élément dans un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
FCMPG | ..., float, float => ..., int | Compare deux nombres | I.6.i.4) Instructions de comparaison |
FCMPL | ..., float, float => ..., int | Compare deux nombres | I.6.i.4) Instructions de comparaison |
FCONST float | ... => ..., float | Pousse 0, 1 ou 2 | I.6.g) Instructions pour pousser des constantes |
FDIV | ..., float, float => ..., float | Divise deux nombres | I.6.e) Instructions de calcul |
FLOAD <nom> | ... => ..., float | Lit une variable locale | I.6.b) Instructions de chargement de valeur |
FMUL | ..., float, float => ..., float | Multiplie deux nombres | I.6.e) Instructions de calcul |
FNEG | ..., float => ..., float | Prend l'opposé d'un nombre | I.6.e) Instructions de calcul |
FREM | ..., float, float => ..., float | Calcul le reste de la division entre deux nombres | I.6.e) Instructions de calcul |
FRETURN | ..., float => [] | Retourne un nombre | I.6.d) Instructions de retour |
FSTORE <nom> | ..., float => ... | Écrit dans une variable locale | I.6.c) Instructions de sauvegarde de valeur |
FSUB | ..., float, float => ..., float | Soustrait deux nombres | I.6.e) Instructions de calcul |
GETFIELD <nom/alias> | ..., <object> => ..., <valeur> | Lit un champs | I.6.b) Instructions de chargement de valeur |
GETSTATIC <nom/alias> | ... => ..., <valeur> | Lit un champs | I.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 byte | I.6.f) Instructions pour convertir |
I2C | ..., int => ..., int | Tronque un int en char | I.6.f) Instructions pour convertir |
I2D | ..., int => ..., double | Convertit un int en double | I.6.f) Instructions pour convertir |
I2F | ..., int => ..., float | Convertit un int en float | I.6.f) Instructions pour convertir |
I2L | ..., int => ..., long | Convertit un int en long | I.6.f) Instructions pour convertir |
I2S | ..., int => ..., int | Tronque un int en short | I.6.f) Instructions pour convertir |
IADD | ..., int, int => ..., int | Additionne deux nombres | I.6.e) Instructions de calcul |
IALOAD | ..., int[], int => ..., int | Lit un élément dans un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
IAND | ..., int, int => ..., int | Et logique entre entiers | I.6.e) Instructions de calcul |
IASTORE | ..., int[], int, int => ... | Écrit dans un élément d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
ICONST int | ... => ..., int | Pousse -1, 0, 1, 2, 3, 4 ou 5 | I.6.g) Instructions pour pousser des constantes |
IDIV | ..., int, int => ..., int | Divise deux nombres | I.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 == 0 | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IFGE <label> | ..., int => ... | Saut si >= 0 | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IFGT <label> | ..., int => ... | Saut si > 0 | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IFLE <label> | ..., int => ... | Saut si <= 0 | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IFLT <label> | ..., int => ... | Saut si < 0 | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IFNE <label> | ..., int => ... | Saut si != 0 | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IFNONNULL <label> | ..., object => ... | Saut si != null | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IFNULL <label> | ..., object => ... | Saut si == null | I.6.i.2) Instructions conditionnelles comparant à zéro ou null |
IINC <nom> int | ... => ... | Incrémente une variable locale d'un entier entre -128 et 127 | I.6.h) Instruction pour incrémenter |
ILOAD <nom> | ... => ..., int | Lit une variable locale | I.6.b) Instructions de chargement de valeur |
IMUL | ..., int, int => ..., int | Multiplie deux nombres | I.6.e) Instructions de calcul |
INEG | ..., int => ..., int | Prend l'opposé d'un nombre | I.6.e) Instructions de calcul |
INSTANCEOF <typeStrict> | ..., <object> => ..., int | Vérifie le type d'un objet | I.6.n) Instructions pour "caster" des objets |
INVOKESPECIAL <signatureMéthode> | ..., <object>, (<valeur>)* => ... ..., <object>, (<valeur>)* => ..., <valeur> | Invoque une méthode super, constructeur (<init>), ou privée | I.6.j) Instructions pour invoqué des méthodes |
INVOKESTATIC <signatureMéthode> | ..., (<valeur>)* => ... ..., (<valeur>)* => ..., <valeur> | Invoque une méthode statique | I.6.j) Instructions pour invoqué des méthodes |
INVOKEVIRTUAL <signatureMéthode> | ..., <object>, (<valeur>)* => ... ..., <object>, (<valeur>)* => ..., <valeur> | Invoque une méthode non statique | I.6.j) Instructions pour invoqué des méthodes |
IOR | ..., int, int => ..., int | Ou logique sur des entiers | I.6.e) Instructions de calcul |
IREM | ..., int, int => ..., int | Reste de la division | I.6.e) Instructions de calcul |
IRETURN | ..., int => [] | Retourne un entier | I.6.d) Instructions de retour |
ISHL | ..., int, int => ..., int | Fait un décalage binaire vers la gauche | I.6.e) Instructions de calcul |
ISHR | ..., int, int => ..., int | Fait un décalage binaire signé vers la droite | I.6.e) Instructions de calcul |
ISTORE <nom> | ..., int => ... | Écrit un entier sur une variable locale | |
ISUB | ..., int, int => ..., int | Soustrait deux entiers | I.6.e) Instructions de calcul |
IUSHR | ..., int, int => ..., int | Fait un décalage binaire non signé vers la droite | I.6.e) Instructions de calcul |
IXOR | ..., int, int => ..., int | Ou exclusif logique sur des entiers | I.6.e) Instructions de calcul |
JSR <label> | ... => ..., adresse | Saut à une sous routine | I.6.i.6) Instructions de saut de type sous routine |
L2D | ..., long => ..., double | Convertit un long en double | I.6.f) Instructions pour convertir |
L2I | ..., long => ..., int | Convertit un long en int | I.6.f) Instructions pour convertir |
L2F | ..., long => ..., float | Convertit un long en float | I.6.f) Instructions pour convertir |
LADD | ..., long, long => ..., long | Additionne deux entiers | I.6.e) Instructions de calcul |
LALOAD | ..., long[], int => ..., long | Lit en entier d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
LAND | ..., long, long => ..., long | Et logique entre deux entiers | I.6.e) Instructions de calcul |
LASTORE | ..., long[], int, long => ... | Écrit un entier dans un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
LCMP | ...long, long => ..., int | Compare deux entiers | I.6.i.4) Instructions de comparaison |
LCONST int | ... => ..., long | Pousse 0 ou 1 | I.6.g) Instructions pour pousser des constantes |
LDC <T1> | ... => ..., <T1> | Pousse une constante | I.6.g) Instructions pour pousser des constantes |
LDC_W <T1> | ... => ..., <T1> | Pousse une constante | I.6.g) Instructions pour pousser des constantes |
LDC2_W <T2> | ... => ..., <T2> | Pousse une constante | I.6.g) Instructions pour pousser des constantes |
LDIV | ..., long, long => ..., long | Divise deux entiers | I.6.e) Instructions de calcul |
LLOAD <nom> | ... => ..., long | Lit une variable locale | I.6.b) Instructions de chargement de valeur |
LMUL | ..., long, long => ..., long | Multiplie deux entiers | I.6.e) Instructions de calcul |
LNEG | ..., long => ..., long | Prend l'opposé d'un entier | I.6.e) Instructions de calcul |
LOOKUPSWITCH (int <label>)* <label> | ..., int => ... | Saut selon une valeur | I.6.i.5) Instructions de saut switch |
LOR | ..., long, long => ..., long | Ou logique entre deux entiers | I.6.e) Instructions de calcul |
LREM | ..., long, long => ..., long | Reste de la division entre deux entiers | I.6.e) Instructions de calcul |
LRETURN | ..., long => [] | Retourne un entier | I.6.d) Instructions de retour |
LSHL | ..., long, int => ..., long | Décalage binaire vers la gauche | I.6.e) Instructions de calcul |
LSHR | ..., long, int => ..., long | Décalage binaire signé vers la droite | I.6.e) Instructions de calcul |
LSTORE <nom> | ..., long => ... | Écrit un entier sur une variable locale | I.6.c) Instructions de sauvegarde de valeur |
LSUB | ..., long, long => ..., long | Soustrait deux entiers | I.6.e) Instructions de calcul |
LUSHR | ..., long, int => ..., long | Décalage binaire non signé vers la droite | I.6.e) Instructions de calcul |
LXOR | ..., long, long => ..., long | Ou exclusif logique entre deux entiers | I.6.e) Instructions de calcul |
MULTIANEWARRAY <type> int | ..., (int)+ => ..., <array> | Créer un tableau à plusieurs dimensions | I.6.l.1) Instructions pour la création de tableaux |
NEW< <nomClasse> | ... => ..., <object> | Crée une nouvelle référence | I.6.k) Instruction pour la création d'objet |
NEWARRAY <primitif> | ..., int => <array> | Crée un nouveau tableau | I.6.l.1) Instructions pour la création de tableaux |
POP | ..., <T1> => ... | Consomme un élément de la pile | I.6.m) Instructions de manipulation de la pile |
POP2 | ..., <T2> => ... | Consomme un élément de la pile | I.6.m) Instructions de manipulation de la pile |
PUSH <valeur> | ... => ..., <valeur> | Pousse une constante | I.6.g) Instructions pour pousser des constantes |
PUTFIELD <nom/alias> | ..., <object>, <valeur> => ... | Écrit un valeur dans un champs non statique | I.6.c) Instructions de sauvegarde de valeur |
PUTSATIC <nom/alias> | ..., <valeur> => ... | Écrit un valeur dans un champs statique | I.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 retour | I.6.d) Instructions de retour |
SALOAD | ..., short[], int => ..., int | Lit un élément d'un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
SASTORE | ..., short[], int, int => ... | Écrit un élément dans un tableau | I.6.l.2) Instructions pour lire, écrire dans les tableaux |
SIPUSH int | ... => int | Pousse un entier entre -32768 et 32767 | I.6.g) Instructions pour pousser des constantes |
SWAP | ..., <T1>1, <T1>2 => ..., , <T1>2, <T1>1 | Échange deux éléments de la pile | I.6.m) Instructions de manipulation de la pile |
SWITCH (int <label>)* <label> | ..., int => ... | Saut selon une valeur | I.6.i.5) Instructions de saut switch |
TABLESWITCH (int <label>)* <label> | ..., int => ... | Saut selon une valeur | I.6.i.5) Instructions de saut switch |