Pohled pod kapotu JVM – využití nástroje Javassist pro generování bajtkódu

Pavel Tišnovský 2. 7. 2013

V dnešní části seriálu o programovacím jazyce Java i o virtuálním stroji Javy si ukážeme některé základní možnosti nástroje Javassist. Tento nástroj lze použít pro programové generování bajtkódu, popř. pro cílenou modifikaci bajtkódů již existujících tříd. Dnes si ukážeme zejména způsob vytváření třídních atributů.

Obsah

1. Využití nástroje Javassist pro programové generování bajtkódu

2. Přidání třídních atributů do generovaného bajtkódu

3. Modifikátory atributů

4. Zdrojový kód demonstračního příkladu ClassGenerationTest2

5. Výsledná podoba bajtkódu třídy vygenerované příkladem ClassGenerationTest2

6. Vytvoření atributu typu pole

7. Zdrojový kód demonstračního příkladu ClassGenerationTest3

8. Výsledná podoba bajtkódu třídy vygenerované příkladem ClassGenerationTest3

9. Repositář se zdrojovými kódy obou demonstračních příkladů

10. Odkazy na Internetu

1. Využití nástroje Javassist pro programové generování bajtkódu

V dnešní části seriálu o programovacím jazyku Java i o virtuálním stroji Javy si ukážeme, jakým způsobem je možné použít nástroj Javassist pro vytváření bajtkódu, konkrétně pro přidávání třídních (tj. statických) atributů do vytvářeného bajtkódu. Připomeňme si, že Javassist je knihovna částečně podobná knihovně BCEL. Obě tyto knihovny nabízí vývojářům programové rozhraní (API) určené pro vytváření či pro modifikaci bajtkódu jednotlivých tříd či rozhraní. V případě knihovny Javassist mají programátoři k dispozici jak vysokoúrovňové konstrukce (například vytvoření metody z řetězce), tak i konstrukce nízkoúrovňové (postupné doplňování jednotlivých „strojových“ instrukcí JVM do těl metod). Vysokoúrovňové i nízkoúrovňové konstrukce je samozřejmě možné navzájem kombinovat, čehož je možné využít například při programování interpretrů či překladačů. My se dnes budeme zabývat převážně vysokoúrovňovými konstrukcemi, které jsou při vytváření bajtkódu jednodušeji použitelné.

Začněme popisem toho, jak se vytvoří bajtkód prázdné třídy. To je v případě použití Javassistu velmi jednoduché:

import java.io.IOException;
import javassist.*;
 
    private static void generateClass() throws CannotCompileException, NotFoundException, IOException {
        // ziskat vychozi class pool
        ClassPool pool = ClassPool.getDefault();
 
        // vytvoreni nove verejne tridy
        CtClass generatedClass = pool.makeClass("TestClass");
 
        // ulozeni bajtkodu na disk
        generatedClass.writeFile();
    }

V tomto kódu se objevuje především třída ClassPool. Instance této třídy slouží jako kontejnery pro objekty typu CtClass představující jednotlivé generované či modifikované třídy (resp. jejich bajtkódy). Sice je možné vytvořit instanci třídy ClassPool s využitím konstruktoru, ovšem vzhledem k tomu, že ve většině případů si vystačíme s jedinou instancí této třídy, lze namísto toho použít metodu ClassPool.getDefault(), která vrátí (a popř. i při svém prvním zavolání vytvoří) výchozí kontejner pro objekty CtClass.

Dále můžeme v ukázaném zdrojovém kódu nalézt volání metody ClassPool.makeClass(jméno_třídy). Tato přetížená metoda slouží k vytvoření nové třídy se zadaným jménem. V nástroji Javassist je každá třída reprezentována objektem typu CtClass. Poslední volanou metodou je CtClass.writeFile(), která se pokusí uložit bajtkód třídy do souboru. Jméno souboru se samozřejmě nemusí zadávat, protože je odvozeno od jména třídy podle zásad popsaných ve specifikaci programovacího jazyka Java (samotný nástroj Javassist navíc provádí základní kontrolu, zda je zadáno korektní jméno třídy).

2. Přidání třídních atributů do generovaného bajtkódu

Do vytvářeného bajtkódu lze snadno přidat i třídní (statické) atributy. Zatímco každá třída je v nástroji Javassist představována objektem typu CtClass, je každý statický i nestatický atribut představován objektem typu CtField. Vytvoření nového atributu se provede přes konstruktor třídy CtField(), který je přetížený a lze ho volat ve dvou variantách:

public CtField(CtField src, CtClass declaring)
public CtField(CtClass type, String name, CtClass declaring)

My využijeme druhou podobu tohoto konstruktoru, při jehož volání se předává typ vytvářeného atributu, jeho jméno a odkaz na třídu, v níž bude atribut deklarován. Jméno nového atributu je reprezentováno řetězcem, třída, v níž je atribut deklarován, je typu CtClass a typ atributu je taktéž představován instancí třídy CtClass. Javassist nám v případě použití atributů primitivních datových typů může pomoci, protože již obsahuje instance příslušných „typových“ tříd:

public abstract class CtClass {
...
...
...
    public static CtClass booleanType;
    public static CtClass charType;
    public static CtClass byteType;
    public static CtClass shortType;
    public static CtClass intType;
    public static CtClass longType;
    public static CtClass floatType;
    public static CtClass doubleType;
    public static CtClass voidType;
...
...
...
}

Jakmile je atribut vytvořen, tj. jakmile jsme získali instanci třídy CtField, je možné ho přidat do vytvářeného bajtkódu s využitím přetížené metody CtClass.addField():

CtClass.addField(CtField f)
CtClass.addField(CtField f, String init)
CtClass.addField(CtField f, CtField.Initializer init)

My v našem příkladu využijeme druhou variantu této metody, která nabízí možnost inicializovat atribut hodnotou předanou formou řetězce (při vytváření atributu se tento řetězec parsuje a kontroluje, zda skutečně obsahuje korektní data). Podívejme se nyní na způsob přidání tří atributů do vytvářené třídy. Prozatím se bude jednat o nestatické atributy. Každému atributu je při přidávání do vytvářeného bajtkódu přiřazena hodnota předaná sice formou řetězce, ovšem ve skutečnosti se provede převod tohoto řetězce na zadaný typ a dokonce se při parsování řetězce provádí například náhrada „1/2f“ za skutečnou hodnotu 0.5f:

    /**
     * Pridani novych atributu do vytvarene tridy.
     *
     * @param generatedClass
     *            predstavuje vytvarenou tridu
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu ci generovanem kodu
     */
    private static void addStaticVariables(CtClass generatedClass) throws CannotCompileException {
        CtField intAttribute = new CtField(CtClass.intType, "intAttribute", generatedClass);
        generatedClass.addField(intAttribute, "42");
 
        CtField floatAttribute = new CtField(CtClass.floatType, "floatAttribute", generatedClass);
        generatedClass.addField(floatAttribute, "1/2f");
 
        CtField booleanAttribute = new CtField(CtClass.booleanType, "booleanAttribute", generatedClass);
        generatedClass.addField(booleanAttribute, "true");
    }

3. Modifikátory atributů

V předchozí kapitole jsme si ukázali, jak se vytvoří nestatické atributy. My však potřebujeme vytvořit atributy třídní (statické) a navíc ještě pro demonstraci možností knihovny Javassist budeme chtít změnit přístupová práva k těmto atributům. Ve skutečnosti je to velmi jednoduché, protože třída CtField (představující atributy v generovaném bajtkódu) nabízí metodu:

public void setModifiers(int mod)

Určenou pro nastavení takzvaných modifikátorů atributů. Číslo předané této metodě je složeno z několika bitových příznaků, které najdeme ve třídě Modifier:

    public static final int PUBLIC    = AccessFlag.PUBLIC;
    public static final int PRIVATE   = AccessFlag.PRIVATE;
    public static final int PROTECTED = AccessFlag.PROTECTED;
    public static final int STATIC    = AccessFlag.STATIC;
    public static final int FINAL     = AccessFlag.FINAL;
    public static final int SYNCHRONIZED = AccessFlag.SYNCHRONIZED;
    public static final int VOLATILE  = AccessFlag.VOLATILE;
    public static final int VARARGS = AccessFlag.VARARGS;
    public static final int TRANSIENT = AccessFlag.TRANSIENT;
    public static final int NATIVE    = AccessFlag.NATIVE;
    public static final int INTERFACE = AccessFlag.INTERFACE;
    public static final int ABSTRACT  = AccessFlag.ABSTRACT;
    public static final int STRICT    = AccessFlag.STRICT;
    public static final int ANNOTATION = AccessFlag.ANNOTATION;
    public static final int ENUM      = AccessFlag.ENUM;

Konkrétní hodnoty jsou uschovány v další třídě AccessFlag:

    public static final int PUBLIC    = 0x0001;
    public static final int PRIVATE   = 0x0002;
    public static final int PROTECTED = 0x0004;
    public static final int STATIC    = 0x0008;
    public static final int FINAL     = 0x0010;
    public static final int SYNCHRONIZED = 0x0020;
    public static final int VOLATILE  = 0x0040;
    public static final int BRIDGE    = 0x0040;     // for method_info
    public static final int TRANSIENT = 0x0080;
    public static final int VARARGS   = 0x0080;     // for method_info
    public static final int NATIVE    = 0x0100;
    public static final int INTERFACE = 0x0200;
    public static final int ABSTRACT  = 0x0400;
    public static final int STRICT    = 0x0800;
    public static final int SYNTHETIC = 0x1000;
    public static final int ANNOTATION = 0x2000;
    public static final int ENUM      = 0x4000;

Tyto hodnoty přesně odpovídají hodnotám ukládaným do bajtkódu a setkali jsme se s nimi například již ve 21. části tohoto seriálu ve třetí kapitole. Na základě předchozích informací již můžeme upravit metodu pro vytváření atributů takovým způsobem, aby se vytvářely statické atributy se zvolenými právy přístupu:

    /**
     * Pridani novych tridnich atributu do vytvarene tridy.
     *
     * @param generatedClass
     *            predstavuje vytvarenou tridu
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu ci generovanem kodu
     */
    private static void addStaticVariables(CtClass generatedClass) throws CannotCompileException {
        CtField intAttribute = new CtField(CtClass.intType, "intAttribute", generatedClass);
        intAttribute.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
        generatedClass.addField(intAttribute, "42");
 
        CtField floatAttribute = new CtField(CtClass.floatType, "floatAttribute", generatedClass);
        floatAttribute.setModifiers(Modifier.STATIC | Modifier.PRIVATE);
        generatedClass.addField(floatAttribute, "1/2f");
 
        CtField booleanAttribute = new CtField(CtClass.booleanType, "booleanAttribute", generatedClass);
        booleanAttribute.setModifiers(Modifier.STATIC | Modifier.FINAL | Modifier.PROTECTED);
        generatedClass.addField(booleanAttribute, "true");
    }

4. Zdrojový kód demonstračního příkladu ClassGenerationTest2

Konečně se dostáváme k dnešnímu prvnímu demonstračnímu příkladu nazvanému ClassGenerationTest2 (dvojka je zde proto, abychom tento příklad odlišili od kódu uvedeného minule). V tomto příkladu se nejdříve vytvoří kostra bajtkódu nové třídy nazvané GeneratedClass2, a posléze se do této třídy vloží trojice statických atributů pojmenovaných intAttribute, floatAttribute a booleanAttribute. Následně je do bajtkódu třídy vloženo i přeložené tělo metody main(), která hodnotu všech tří třídních atributů vypíše na standardní výstup:

import java.io.IOException;
 
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
 
 
 
/**
 * Test moznosti nastroje Javassist - vygenerovani jednoduche tridy
 * s metodou main a nekolika tridnimi (statickymi) atributy.
 *
 * @author Pavel Tisnovsky
 */
public class ClassGenerationTest2 {
 
    /**
     * Jmeno vygenerovane tridy.
     */
    private static final String GENERATED_CLASS_NAME = "GeneratedClass2";
 
    /**
     * Zdrojovy kod metody main(), ktery bude nasledne zkompilovan
     * do bajtkodu a zakomponovan do vytvorene tridy.
     */
    private static final String MAIN_METHOD_SOURCE_TEXT =
        "public static void main(String[] args)" +
        "{" +
        "    System.out.println(intAttribute);" +
        "    System.out.println(floatAttribute);" +
        "    System.out.println(booleanAttribute);" +
        "}";
 
    /**
     * Vytvoreni metody main() z jejiho zdrojoveho kodu.
     * 
     * @param generatedClass
     *            predstavuje vytvarenou tridu
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu
     */
    private static void addMainMethod(CtClass generatedClass) throws CannotCompileException {
        CtMethod methodMain = CtMethod.make(MAIN_METHOD_SOURCE_TEXT, generatedClass);
        generatedClass.addMethod(methodMain);
    }
 
    /**
     * Pridani novych tridnich atributu do vytvarene tridy.
     *
     * @param generatedClass
     *            predstavuje vytvarenou tridu
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu ci generovanem kodu
     */
    private static void addStaticVariables(CtClass generatedClass) throws CannotCompileException {
        CtField intAttribute = new CtField(CtClass.intType, "intAttribute", generatedClass);
        intAttribute.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
        generatedClass.addField(intAttribute, "42");
 
        CtField floatAttribute = new CtField(CtClass.floatType, "floatAttribute", generatedClass);
        floatAttribute.setModifiers(Modifier.STATIC | Modifier.PRIVATE);
        generatedClass.addField(floatAttribute, "1/2f");
 
        CtField booleanAttribute = new CtField(CtClass.booleanType, "booleanAttribute", generatedClass);
        booleanAttribute.setModifiers(Modifier.STATIC | Modifier.FINAL | Modifier.PROTECTED);
        generatedClass.addField(booleanAttribute, "true");
    }
 
    /**
     * Vytvoreni tridy s metodou main().
     * 
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu metody main()
     * @throws IOException
     *             pokud dojde k chybe pri zapisu bajtkodu na disk
     * @throws NotFoundException
     *             pokud dojde k chybe pri zapisu bajtkodu na disk
     */
    private static void generateClass() throws CannotCompileException, NotFoundException, IOException {
        // ziskat vychozi class pool
        ClassPool pool = ClassPool.getDefault();
 
        // vytvoreni nove verejne tridy
        CtClass generatedClass = pool.makeClass(GENERATED_CLASS_NAME);
        // pridani tridnich atributu
        addStaticVariables(generatedClass);
        // pridani metody do teto tridy
        addMainMethod(generatedClass);
 
        // ulozeni bajtkodu na disk
        generatedClass.writeFile();
    }
 
    /**
     * Spusteni generatoru tridy.
     *
     * @param args nevyuzito
     */
    public static void main(String[] args) {
        System.out.println("class generation begin: " + GENERATED_CLASS_NAME);
        try {
            generateClass();
        }
        catch (CannotCompileException e) {
            e.printStackTrace();
        }
        catch (NotFoundException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("class generation end: " + GENERATED_CLASS_NAME);
    }
 
}

5. Výsledná podoba bajtkódu třídy vygenerované příkladem ClassGenerationTest2

Demonstrační příklad ClassGenerationTest2 se přeloží následujícím skriptem, jenž předpokládá přítomnost archivu javassist.jar:

javac -cp javassist.jar ClassGenerationTest2.java

Spuštění se provede příkazem:

java -cp .:javassist.jar ClassGenerationTest2

Po spuštění by se v aktuálním adresáři měl vytvořit soubor GeneratedClass2.class obsahující nově vytvořený bajtkód. Jeho obsah je možné si prohlédnout po zadání příkazu:

javap -c -private GeneratedClass2
Compiled from "GeneratedClass2.java"
public class GeneratedClass2 extends java.lang.Object{
 
public static int intAttribute;
 
private static float floatAttribute;
 
protected static final boolean booleanAttribute;
 
public static void main(java.lang.String[]);
  Code:
   0:   getstatic   #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   getstatic   #22; //Field intAttribute:I
   6:   invokevirtual   #28; //Method java/io/PrintStream.println:(I)V
   9:   getstatic   #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   12:  getstatic   #30; //Field floatAttribute:F
   15:  invokevirtual   #33; //Method java/io/PrintStream.println:(F)V
   18:  getstatic   #20; //Field java/lang/System.out:Ljava/io/PrintStream;
   21:  getstatic   #35; //Field booleanAttribute:Z
   24:  invokevirtual   #38; //Method java/io/PrintStream.println:(Z)V
   27:  return
 
public GeneratedClass2();
  Code:
   0:   aload_0
   1:   invokespecial   #43; //Method java/lang/Object."<;init>":()V
   4:   return
 
static {};
  Code:
   0:   bipush  42
   2:   putstatic   #22; //Field intAttribute:I
   5:   ldc #44; //float 0.5f
   7:   putstatic   #30; //Field floatAttribute:F
   10:  iconst_1
   11:  putstatic   #35; //Field booleanAttribute:Z
   14:  return
 
}
 

Povšimněte si především modifikátorů všech tří atributů i způsobu jejich inicializace ve statickém bloku (blok označený static{}). Tímto způsobem se inicializují statické atributy i při použití běžného překladače Javy, tj. při použití nástroje javac.

6. Vytvoření atributu typu pole

S tvorbou atributů primitivních datových typů jsme se seznámili v předchozích kapitolách, z nichž je taktéž patrné, že se nejedná o nijak složitou problematiku – přidání a popř. i inicializace atributu je otázkou tří či čtyř volání metod ze tříd, které jsou součástí nástroje Javassist. Poněkud složitější je však situace ve chvíli, kdy je nutné vytvořit atribut objektového typu, popř. atribut typu pole. Podívejme se nejdříve na způsob vytvoření polí. Pole jsou zde chápána jako další typ tříd, takže musíme být schopni získat obraz této třídy představovaný instancí CtClass. Pro tento účel je možné použít již výše zmíněný ClassPool, konkrétně jeho metodu get(), které se předá textová podoba pole:

        CtClass arrayClass = pool.get("int[]");

Ve chvíli, kdy máme k dispozici instanci CtClass představující obraz pole (zde pole celých čísel), je již snadné do vytvářeného bajtkódu přidat nový atribut tohoto typu:

        CtField array = new CtField(arrayClass, "array", generatedClass);
        generatedClass.addField(array);

Celá metoda sloužící pro přidání pole jako statického atributu vytvářené třídy má tvar:

    private static void addStaticArray(ClassPool pool, CtClass generatedClass) throws CannotCompileException, NotFoundException {
        CtClass arrayClass = pool.get("int[]");
        CtField array = new CtField(arrayClass, "array", generatedClass);
        array.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
        generatedClass.addField(array);
    }

7. Zdrojový kód demonstračního příkladu ClassGenerationTest3

Dnešní druhý demonstrační příklad, jehož jméno je ClassGenerationTest3, se od předchozího demonstračního příkladu odlišuje pouze v tom ohledu, že se namísto trojice atributů primitivních datových typů vytváří jediný atribut představující neinicializované pole celých čísel. Namísto metody addStaticVariables() je zde použita mírně odlišná metoda addStaticArray() popsaná v předchozí kapitole. Následuje úplný výpis zdrojového kódu tohoto demonstračního příkladu:

import java.io.IOException;
 
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.NotFoundException;
 
 
 
/**
 * Test moznosti nastroje Javassist - vygenerovani jednoduche tridy
 * s metodou main a jednim tridnim atributem - polem.
 *
 * @author Pavel Tisnovsky
 */
public class ClassGenerationTest3 {
 
    /**
     * Jmeno vygenerovane tridy.
     */
    private static final String GENERATED_CLASS_NAME = "GeneratedClass3";
 
    /**
     * Zdrojovy kod metody main(), ktery bude nasledne zkompilovan
     * do bajtkodu a zakomponovan do vytvorene tridy.
     */
    private static final String MAIN_METHOD_SOURCE_TEXT =
        "public static void main(String[] args)" +
        "{" +
        "    System.out.println(array);" +
        "}";
 
    /**
     * Vytvoreni metody main() z jejiho zdrojoveho kodu.
     * 
     * @param generatedClass
     *            predstavuje vytvarenou tridu
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu
     */
    private static void addMainMethod(CtClass generatedClass) throws CannotCompileException {
        CtMethod methodMain = CtMethod.make(MAIN_METHOD_SOURCE_TEXT, generatedClass);
        generatedClass.addMethod(methodMain);
    }
 
    /**
     * Pridani noveho tridniho atributu do vytvarene tridy - pole.
     * @param pool 
     *
     * @param generatedClass
     *            predstavuje vytvarenou tridu
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu ci generovanem kodu
     * @throws NotFoundException
     *             vyhozena v pripade, ze se nenajde trida odpovidajici zapisu "int[]"
     */
    private static void addStaticArray(ClassPool pool, CtClass generatedClass) throws CannotCompileException, NotFoundException {
        CtClass arrayClass = pool.get("int[]");
        CtField array = new CtField(arrayClass, "array", generatedClass);
        array.setModifiers(Modifier.STATIC | Modifier.PUBLIC);
        generatedClass.addField(array);
    }
 
    /**
     * Vytvoreni tridy s metodou main().
     * 
     * @throws CannotCompileException
     *             vyhozena v pripade chyby ve zdrojovem kodu metody main()
     * @throws IOException
     *             pokud dojde k chybe pri zapisu bajtkodu na disk
     * @throws NotFoundException
     *             pokud dojde k chybe pri zapisu bajtkodu na disk
     */
    private static void generateClass() throws CannotCompileException, NotFoundException, IOException {
        // ziskat vychozi class pool
        ClassPool pool = ClassPool.getDefault();
 
        // vytvoreni nove verejne tridy
        CtClass generatedClass = pool.makeClass(GENERATED_CLASS_NAME);
        // pridani tridnich atributu
        addStaticArray(pool, generatedClass);
        // pridani metody do teto tridy
        addMainMethod(generatedClass);
 
        // ulozeni bajtkodu na disk
        generatedClass.writeFile();
    }
 
    /**
     * Spusteni generatoru tridy.
     *
     * @param args nevyuzito
     */
    public static void main(String[] args) {
        System.out.println("class generation begin: " + GENERATED_CLASS_NAME);
        try {
            generateClass();
        }
        catch (CannotCompileException e) {
            e.printStackTrace();
        }
        catch (NotFoundException e) {
            e.printStackTrace();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("class generation end: " + GENERATED_CLASS_NAME);
    }
 
}

8. Výsledná podoba bajtkódu třídy vygenerované příkladem ClassGenerationTest3

Překlad dnešního druhého demonstračního příkladu se provede podobným způsobem, s jakým jsme se již setkali u příkladu předchozího:

javac -cp javassist.jar ClassGenerationTest3.java

I jeho spuštění je téměř shodné:

java -cp .:javassist.jar ClassGenerationTest2

Zajímavější je struktura vygenerovaného bajtkódu třídy GeneratedClass3. Tuto strukturu si opět prohlédneme pomocí standardního JDK nástroje javap:

javap -c -private GeneratedClass3

Z výpisu je patrné, že je skutečně vytvořen statický atribut pojmenovaný array, který je typu pole celých čísel (int[]), ovšem nedošlo k žádné inicializaci tohoto atributu:

widgety

Compiled from "GeneratedClass3.java"
public class GeneratedClass3 extends java.lang.Object{
 
public static int[] array;
 
public static void main(java.lang.String[]);
  Code:
   0:   getstatic   #16; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   getstatic   #18; //Field array:[I
   6:   invokevirtual   #24; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
   9:   return
 
public GeneratedClass3();
  Code:
   0:   aload_0
   1:   invokespecial   #29; //Method java/lang/Object."":()V
   4:   return
 
}

Jakým způsobem lze tuto inicializaci provést, si – společně s dalšími informacemi – ukážeme příště.

9. Repositář se zdrojovými kódy obou demonstračních příkladů

Zdrojové kódy obou dnes popsaných demonstračních příkladů ClassGenerationTest2 a ClassGenerationTest3 jsou, společně s pomocnými skripty, uloženy do Mercurial repositáře dostupného na adrese http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/. V následující tabulce najdete odkazy na prozatím nejnovější verze těchto zdrojových kódů:

10. Odkazy na Internetu

  1. Open Source ByteCode Libraries in Java
    http://java-source.net/open-source/bytecode-libraries
  2. ASM Home page
    http://asm.ow2.org/
  3. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  4. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  5. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  6. BCEL Home page
    http://commons.apache.org/bcel/
  7. Byte Code Engineering Library (před verzí 5.0)
    http://bcel.sourceforge.net/
  8. Byte Code Engineering Library (verze >= 5.0)
    http://commons.apache.org/pro­per/commons-bcel/
  9. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  10. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  11. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  12. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  13. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  14. Javassist
    http://www.jboss.org/javassist/
  15. Byteman
    http://www.jboss.org/byteman
  16. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  17. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  18. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  19. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  20. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  21. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  22. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  23. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  24. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  25. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  26. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  27. Cobertura
    http://cobertura.sourceforge.net/
  28. jclasslib bytecode viewer
    http://www.ej-technologies.com/products/jclas­slib/overview.html
Našli jste v článku chybu?
Podnikatel.cz: Poslanci chtějí sebrat majetek Bakalovi

Poslanci chtějí sebrat majetek Bakalovi

DigiZone.cz: Nova opět stahuje „milionáře“

Nova opět stahuje „milionáře“

Vitalia.cz: dTest odhalil ten nejlepší kečup

dTest odhalil ten nejlepší kečup

Vitalia.cz: Muž, který miluje příliš. Ženám neimponuje

Muž, který miluje příliš. Ženám neimponuje

DigiZone.cz: Sony MP-CL1A: miniaturní projektor

Sony MP-CL1A: miniaturní projektor

DigiZone.cz: Ginx TV: pořad o počítačových hráčích

Ginx TV: pořad o počítačových hráčích

DigiZone.cz: Funbox 4K v DVB-T2 má ostrý provoz

Funbox 4K v DVB-T2 má ostrý provoz

Vitalia.cz: 5 chyb, které děláme při skladování potravin

5 chyb, které děláme při skladování potravin

DigiZone.cz: Parlamentní listy: kde končí PR...

Parlamentní listy: kde končí PR...

Lupa.cz: Další Češi si nechali vložit do těla čip

Další Češi si nechali vložit do těla čip

DigiZone.cz: Wimbledon na Nova Sport až do 2019

Wimbledon na Nova Sport až do 2019

Vitalia.cz: Tahák, jak vyzrát nad zápachem z úst

Tahák, jak vyzrát nad zápachem z úst

Vitalia.cz: Jak Ondra o astma přišel

Jak Ondra o astma přišel

Lupa.cz: Cimrman má hry na YouTube i vlastní doodle

Cimrman má hry na YouTube i vlastní doodle

Podnikatel.cz: EET pro e-shopy? Postavené na hlavu

EET pro e-shopy? Postavené na hlavu

Lupa.cz: Aukro.cz mění majitele. Vrací se do českých rukou

Aukro.cz mění majitele. Vrací se do českých rukou

Podnikatel.cz: Babišovi se nedá věřit, stěžovali si hospodští

Babišovi se nedá věřit, stěžovali si hospodští

DigiZone.cz: Samsung EVO-S: novinka pro Skylink

Samsung EVO-S: novinka pro Skylink

DigiZone.cz: DVB-T2 ověřeno: seznam TV zveřejněn

DVB-T2 ověřeno: seznam TV zveřejněn

Podnikatel.cz: Znáte už 5 novinek k #EET

Znáte už 5 novinek k #EET