Le bytecode Java est décrit formellement dans The Java Virtual Machine specification, disponible non seulement en ligne mais aussi à à la bibliothèque.
Le modèle d'exécution est une machine à pile : à tout moment, le programme dispose des variables statiques, des variables du tas, de variables locales (adressées par leur numéro) et d'une pile d'arguments. Chaque opération élémentaire, chaque appel de méthode prend ses arguments dans la pile (et les ôte de celle-ci) et pousse sa valeur de retour dans la pile.
Une particularité : le bytecode travaille sur des valeurs de deux tailles (32 et 64 bits). Les valeurs de 64 bits sont stockées comme deux valeurs de 32 consécutives ; ce qui veut dire que la valeur de 64 bits stockée à l'emplacement n occupe les emplacements n et n+1.
Nous recommandons l'utilisation de la bibliothèque ASM, dont nous fournissons la documentation (archive à télécharger) et une archive JAR déjà compilée. Pour s'en servir, il suffit donc de rajouter asm-2.2-all.jar à CLASSPATH.
Cette bibliothèque permet de construire du bytecode Java à l'aide d'une suite d'appels de méthodes. Il existe un tutoriel d'apprentissage de cette bibliothèque, mais de loin la solution la plus simple est d'utiliser l'outil ASMifier (livré dans l'archive JAR fournie). Cet outil produit, à partir d'une classe Java quelconque, un fichier source Java contenant une suite d'appels à ASM qui permet de reconstruire la classe. On pourra donc programmer quelques classes simples, les compiler, les passer dans ASMifier et s'inspirer des résultats. Par exemple :
Essai.java
class Essai {
public static double f(double x) {
return Math.sin(x)*x + 3 * x + 1;
}
}
import java.util.*;
import org.objectweb.asm.*;
import org.objectweb.asm.attrs.*;
public class EssaiDump implements Opcodes {
public static byte[] dump () throws Exception {
ClassWriter cw = new ClassWriter(false);
FieldVisitor fv;
MethodVisitor mv;
AnnotationVisitor av0;
cw.visit(V1_5, ACC_SUPER, "Essai", null, "java/lang/Object", nu
ll);
cw.visitSource("Essai.java", null);
{
mv = cw.visitMethod(0, "", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "",
"()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, "f", "(D)D", null,
null);
mv.visitCode();
mv.visitVarInsn(DLOAD, 0);
mv.visitMethodInsn(INVOKESTATIC, "java/lang/Math", "sin", "(D)D
");
mv.visitVarInsn(DLOAD, 0);
mv.visitInsn(DMUL);
mv.visitLdcInsn(new Double("3.0"));
mv.visitVarInsn(DLOAD, 0);
mv.visitInsn(DMUL);
mv.visitInsn(DADD);
mv.visitInsn(DCONST_1);
mv.visitInsn(DADD);
mv.visitInsn(DRETURN);
mv.visitMaxs(6, 2);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
}