Hlavní navigace

Pohled pod kapotu JVM – zpracování výjimek v JVM, Python VM a Lua VM

9. 9. 2014
Doba čtení: 21 minut

Sdílet

Poslední porovnání virtuálního stroje Javy, VM jazyka Python a VM programovacího jazyka Lua se bude týkat způsobu zpracování výjimek. Jedná se o velmi důležitou součást prakticky všech moderních virtuálních strojů a jak vyplyne z dalšího textu, mohou být výjimky zpracovány mnohdy i značně odlišnými způsoby.

Obsah

1. Pohled pod kapotu JVM – zpracování výjimek v JVM, Python VM a Lua VM

2. Porovnání způsobů práce s výjimkami v jazycích Java, Lua a Python

3. Výjimky v programovacím jazyku Java

4. Vyhození výjimky s využitím instrukce athrow

5. Demonstrační příklad Test31.java: vyhození výjimky typu RuntimeException

6. Překlad demonstračního příkladu Test31.java do bajtkódu JVM

7. Demonstrační příklad Test32.java: vyhození výjimky typu Exception

8. Překlad demonstračního příkladu Test32.java do bajtkódu JVM

9. Demonstrační příklad Test33.java: implementace struktury try-catch

10. Překlad demonstračního příkladu Test33.java do bajtkódu JVM

11. Demonstrační příklad Test34.java: implementace struktury try-catch-finally

12. Překlad demonstračního příkladu Test34.java do bajtkódu JVM

13. Repositář se zdrojovými kódy všech sedmi dnešních demonstračních příkladů

14. Odkazy na Internetu

1. Pohled pod kapotu JVM – zpracování výjimek v JVM, Python VM a Lua VM

V předchozích částech tohoto seriálu jsme si popsali již prakticky všechny skupiny instrukcí, které je možné najít v bajtkódech určených pro zpracování (běh) ve virtuálních strojích Javy (JVM), Pythonu (Python VM) i ve virtuálním stroji programovacího jazyka Lua (Lua VM). Ještě si však musíme popsat jednu velmi důležitou vlastnost všech tří zmíněných bajtkódů – jedná se o instrukce a o specializované datové struktury použité při zpracování výjimek i při implementaci bloků try-catch-finally popř. jejich ekvivalentů (to se týká především jazyka Lua, který tyto bloky v současné verzi nepodporuje). Dnes se budeme zabývat převážně virtuálním strojem Javy, ovšem v následující části tohoto seriálu si osvětlíme princip práce s výjimkami i ve virtuálním stroji Pythonu a taktéž Lua VM.

2. Porovnání způsobů práce s výjimkami v jazycích Java, Lua a Python

Nejprve se na problematiku výjimek, včetně způsobu jejich vyhazování i zachycování, podívejme z hlediska programátora vytvářejícího aplikace v Javě, Pythonu či v jazyku Lua. Teprve poté si vysvětlíme způsob práce s výjimkami přímo v bajtkódu.

Začněme popisem práce s výjimkami v Javě. V tomto programovacím jazyku má vývojář k dispozici hned několik klíčových slov určených jak pro označení těch výjimek, které mohou vzniknout v nějaké metodě (klíčové slovo throws), tak i pro samotné programové vyhození výjimky zvoleného typu (klíčové slovo throw) i její zachycení (struktura tvořená dvojicí programových bloků try-catch popř. rozšířená i o blok finally). V prvním příkladu ModuloByZero.java je ukázáno použití bloků try-catch-finally při výpočtu zbytku po dělení i pro korektní reakci na stav, kdy je dělitel nulový. Pokud dojde k dělení nulou (a operace % taktéž provádí dělení), je vyhozena výjimka typu ArithmeticException, která je následně ve stejné metodě zachycena a vhodným způsobem zpracována:

public class Test {
    public static void main(String[] args) {
        int x = 42;
        int y = 0;
        try {
            int z = x % y;
        }
        catch (ArithmeticException e) {
            System.out.println("div/mod by zero detected!");
            System.out.println(e);
        }
        finally {
            System.out.println("done");
        }
    }
}

Prakticky stejným způsobem jsou programové bloky try-catch-finally implementovány i v programovacím jazyku Python, až na jeden nepatrný (syntaktický) rozdíl – namísto klíčového slova catch se zde používá slovo except. Demonstrační příklad ModuloByZero.py, jehož zdrojový kód je umístěn pod tímto odstavcem, se do značné míry podobá výše uvedenému příkladu ModuloByZero.java, ovšem samotný zápis je mnohem stručnější (což se ostatně dá od Pythonu očekávat :-) a navíc i univerzálnější v případě, že bude operátor % přetížen a namísto číselných proměnných xy budou použity objekty, které s operátorem % dokážou korektně pracovat:

x = 42
y = 0
try:
    z = x % y
except ZeroDivisionError, e:
    print "div/mod by zero detected!"
    print e
finally:
    print "done"

Zcela odlišná je však implementace podobného příkladu v programovacím jazyce Lua, a to z toho důvodu, že tento jazyk přímo neobsahuje podporu pro bloky try-catch-finally. Namísto toho je aplikován „funkcionální“ přístup, kdy je ta část programu, v níž může nastat nějaká výjimka, umístěna do samostatné funkce, která se pak zavolá v „sandboxu“ s využitím funkce pcall() (protected call). Výjimka se vyhazuje funkcí error() a pokud výjimka skutečně nastane, vrací funkce pcall() ve svém prvním výsledku hodnotu false (další návratová hodnota či návratové hodnoty již souvisí s volanou funkcí). V demonstračním příkladu ModuloByZero.lua je navíc použit malý trik se zjištěním, zda při dělení či při operaci modulo nedošlo k dělení nulou, protože Lua v tomto případě automaticky výjimku nevyhazuje (zmíněný trik je založen na normě IEEE 754 tvrdící, že dvě hodnoty NaN si nejsou nikdy rovny):

function mod(x, y)
    local result = x % y
    if result ~= result then
        error("ArithmeticException: / by zero")
    end
    return result
end
 
local x = 42
local y = 0
 
local status, result = pcall(mod , x, y)
print(status, result)
 
x = 10
y = 3
 
local status, result = pcall(mod , x, y)
print(status, result)

3. Výjimky v programovacím jazyku Java

V programovacím jazyku Java je zpracování výjimek nedílnou součástí syntaxe i sémantiky, a to již od první verze Javy. Totéž platí o bajtkódu JVM, který obsahuje podporu pro instrukci athrow popsanou ve čtvrté kapitole a navíc se v bajtkódu používají i datové struktury popisující ty části bajtkódu, kde může vzniknout zachycovaná výjimka či výjimky. Díky této podpoře a taktéž s využitím podmíněných a nepodmíněných skoků lze bloky try-catch-finally v bajtkódu implementovat relativně úsporným způsobem, což bude ostatně ukázáno na demonstračních příkladech Test31, Test32, Test33 a konečně Test34.

4. Vyhození výjimky s využitím instrukce athrow

Velmi důležitou instrukcí, kterou můžeme v bajtkódu virtuálního stroje Javy často nalézt, je instrukce nazvaná příznačně athrow. Tato instrukce slouží k vyhození výjimky, přičemž při zavolání této instrukce musí být reference na objekt reprezentující výjimku uložena na vrcholu (TOS) zásobníku operandů (právě z tohoto důvodu začíná jméno instrukce znakem „a“, podobně jako jména prakticky všech instrukcí zpracovávajících reference na objekty a nikoli primitivní datové typy). Tato instrukce nemá žádné další operandy (kromě reference uložené na TOS), takže její celková velikost je přesně jeden bajt:

# Instrukce Opkód Operandy Prováděná operace
1 athrow 0×BF × vyhození výjimky

Bajtkód každé metody navíc může obsahovat tabulku obsahující seznam výjimek, které mohou nastat a adresy instrukcí, na něž se provede skok ve chvíli, kdy k dané výjimce skutečně dojde. Pokud však tato tabulka není přítomna, popř. neobsahuje záznam s daným typem výjimky, je výjimka vyhozena (throw) z metody a může být zachycena v nadřazených metodách, opět s využitím zmíněné tabulky. Podrobnosti si ukážeme v následujících čtyřech demonstračních příkladech.

5. Demonstrační příklad Test31.java: vyhození výjimky typu RuntimeException

Způsob překladu Javovských programů, v nichž se používají a popř. i zpracovávají výjimky, si dnes ukážeme na čtveřici velmi jednoduchých demonstračních příkladů. V prvním příkladu nazvaném Test31 se ve statické metodě pojmenované throwRuntimeException() programově vyhazuje výjimka typu RuntimeException. Tento typ výjimky není zapotřebí zmiňovat v hlavičce metody a dokonce ho není ani nutné v aplikaci explicitně zachytit (tyto kontroly provádí již překladač javac, ovšem RuntimeException a od ní odvozené výjimky nepatří mezi výjimky kontrolované – checked). Pokud ovšem k této výjimce při běhu programu skutečně dojde a výjimka není nikde zachycena, bude běh programu a tím pádem i běh virtuálního stroje Javy ukončen a na terminál se vypíše klasický stack trace (poněkud odlišné je ovšem chování takové aplikace, kde k výjimce dojde v jiném vláknu; zde k ukončení celé JVM nedojde). Podívejme se nyní na zdrojový kód tohoto velmi jednoduchého demonstračního příkladu:

/**
 * Demonstracni priklad cislo 31.
 *
 * Vyhozeni vyjimky typu RuntimeException z testovane metody.
 */
public class Test31 {
 
    /**
     * Metoda, ktera vyhodi vyjimku typu RuntimeException()
     */
    public static void throwRuntimeException() {
        throw new RuntimeException();
    }
 
    /**
     * Zavolani metody throwRuntimeException().
     */
    public static void main(String[] args) {
        throwRuntimeException();
    }
 
}

Po spuštění tohoto demonstračního příkladu se na terminálu objeví informace o výjimce vyhozené z metody throwRuntimeException a následně i z metody main. Tato informace se vypíše ještě předtím, než se ukončí běh virtuálního stroje Javy:

Exception in thread "main" java.lang.RuntimeException
    at Test31.throwRuntimeException(Test31.java:12)
    at Test31.main(Test31.java:19)

6. Překlad demonstračního příkladu Test31.java do bajtkódu JVM

Překlad metody throwRuntimeException() do bajtkódu JVM je velmi jednoduchý. V těle této metody se nejdříve vytvoří a náležitě inicializuje objekt třídy RuntimeException a následně je použita instrukce athrow pro vyhození výjimky. Tato instrukce požaduje, aby na TOS byla umístěna reference na objekt představující výjimku, což je i důvod použití instrukce dup. Povšimněte si, že metoda neobsahuje žádné speciální označení ani metadata říkající, že při jejím běhu může k výjimce skutečně dojít:

  public static void throwRuntimeException();
    Code:
       0: new           #2;                 // vytvoření instance třídy java.lang.RuntimeException
       3: dup                               // kopie reference na nově vytvořený objekt
                                            // inicializace objektu
       4: invokespecial #3;                 // Method java/lang/RuntimeException."<init>":()V
       7: athrow                            // objekt byl inicializován, vyhození výjimky

Ani při volání metody throwRuntimeException() není zapotřebí žádným způsobem specifikovat, že může dojít k výjimce:

)
  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #4;                 // zavolání statické metody throwRuntimeException()
       3: return                            // ukončení běhu metody main()

Pro úplnost je ještě vypsán obsah constant poolu, protože výše uvedené bajtkódy používaly záznamy uložené na indexech 2, 3 a 4 (a samozřejmě i záznamy z těchto záznamů referencované):

Constant pool:
   #1 = Methodref          #6.#16;        //  java/lang/Object."<init>":()V
   #2 = Class              #17;           //  java/lang/RuntimeException
   #3 = Methodref          #2.#16;        //  java/lang/RuntimeException."<init>":()V
   #4 = Methodref          #5.#18;        //  Test31.throwRuntimeException:()V
   #5 = Class              #19;           //  Test31
   #6 = Class              #20;           //  java/lang/Object
   #7 = Utf8               <init>;
   #8 = Utf8               ()V;
   #9 = Utf8               Code;
  #10 = Utf8               LineNumberTable;
  #11 = Utf8               throwRuntimeException;
  #12 = Utf8               main;
  #13 = Utf8               ([Ljava/lang/String;)V;
  #14 = Utf8               SourceFile;
  #15 = Utf8               Test31.java;
  #16 = NameAndType        #7:#8;         //  "<init>":()V
  #17 = Utf8               java/lang/RuntimeException;
  #18 = NameAndType        #11:#8;        //  throwRuntimeException:()V
  #19 = Utf8               Test31;
  #20 = Utf8               java/lang/Object;

7. Demonstrační příklad Test32.java: vyhození výjimky typu Exception

Zdrojový kód demonstračního příkladu Test32.java se do značné míry podobá zdrojovému kódu výše uvedeného příkladu Test31.java. Najdeme zde však tři odlišnosti. První odlišnost spočívá v tom, že se namísto výjimky typu RuntimeException programově vyhazuje výjimka typu Exception. S tím souvisí i další dvě změny – výjimky typu Exception (a samozřejmě i všechny odvozené výjimky) je zapotřebí buď explicitně zachytit nebo (opět explicitně) u metod, kde může výjimka vzniknout, použít zápis throws Exception, aby tak vývojář dal překladači Javy najevo, že se zpracováním výjimky tohoto typu ve své aplikaci počítá. Právě z tohoto důvodu jsou v hlavičkách obou statických funkcí deklarovaných ve třídě Test32 použity zápisy throws Exception; v opačném případě by nebylo možné provést úspěšný překlad tohoto příkladu:

/**
 * Demonstracni priklad cislo 32.
 *
 * Vyhozeni vyjimky typu Exception z testovane metody.
 */
public class Test32 {
 
    /**
     * Metoda, ktera vyhodi vyjimku typu Exception()
     */
    public static void throwException() throws Exception {
        throw new Exception();
    }
 
    /**
     * Zavolani metody throwException().
     */
    public static void main(String[] args) throws Exception {
        throwException();
    }
 
}

I po spuštění tohoto demonstračního příkladu dojde k ukončení běhu virtuálního stroje Javy. Opět se vypíše i historie volání metod uložených v jednotlivých zásobníkových rámcích:

Exception in thread "main" java.lang.Exception
    at Test32.throwException(Test32.java:12)
    at Test32.main(Test32.java:19)

8. Překlad demonstračního příkladu Test32.java do bajtkódu JVM

Velmi zajímavé bude zjistit, do jaké míry se budou odlišovat bajtkódy tříd Test31Test32. Překlad metody throwException():

  public static void throwException() throws java.lang.Exception;
    Code:
       0: new           #2;                 // vytvoření instance třídy java.lang.Exception
       3: dup                               // kopie reference na nově vytvořený objekt
                                            // inicializace objektu
       4: invokespecial #3;                 // Method java/lang/Exception."<init>":()V
       7: athrow                            // objekt byl inicializován, vyhození výjimky

Vidíme, že jediný rozdíl spočívá v přidané deklaraci „throws java.lang.Exception;“, což lze považovat za metadata bajtkódu.

Překlad metody main():

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
       0: invokestatic  #4;                 // zavolání statické metody throwException:()V
       3: return                            // ukončení běhu metody main()

I zde došlo pouze k přidání deklarace „throws java.lang.Exception“. Žádné další změny v bajtkódu nenajdeme.

Na závěr bude opět vypsán obsah constant poolu bajtkódu třídy Test32:

   #1 = Methodref          #6.#17;        //  java/lang/Object."<init>":()V
   #2 = Class              #18;           //  java/lang/Exception
   #3 = Methodref          #2.#17;        //  java/lang/Exception."<init>":()V
   #4 = Methodref          #5.#19;        //  Test32.throwException:()V
   #5 = Class              #20;           //  Test32
   #6 = Class              #21;           //  java/lang/Object
   #7 = Utf8               <init>;
   #8 = Utf8               ()V;
   #9 = Utf8               Code;
  #10 = Utf8               LineNumberTable;
  #11 = Utf8               throwException;
  #12 = Utf8               Exceptions;    *** přidaný záznam ***
  #13 = Utf8               main;
  #14 = Utf8               ([Ljava/lang/String;)V;
  #15 = Utf8               SourceFile;
  #16 = Utf8               Test32.java;
  #17 = NameAndType        #7:#8;         //  "<init>":()V
  #18 = Utf8               java/lang/Exception;
  #19 = NameAndType        #11:#8;        //  throwException:()V
  #20 = Utf8               Test32;
  #21 = Utf8               java/lang/Object;

9. Demonstrační příklad Test33.java: implementace struktury try-catch

Demonstrační příklad nazvaný Test33.java se od obou předchozích demonstračních příkladů odlišuje, protože zde výjimku vyhazovanou z metody pojmenované throwException() zachycujeme, a to (samozřejmě) v bloku catch, kterému předchází blok try. Vzhledem k tomu, že výjimka je při volání statické metody throwException() skutečně vyhozena vždy, není příkaz „System.out.println(„try block“);“ umístěný v bloku try nikdy proveden; namísto toho je naopak vždy proveden příkaz „System.out.println(„catch block“);“ umístěný v bloku catch:

/**
 * Demonstracni priklad cislo 33.
 *
 * Implementace struktury try-catch.
 */
public class Test33 {
 
    /**
     * Metoda, ktera vyhodi vyjimku typu Exception()
     */
    public static void throwException() throws Exception {
        throw new Exception();
    }
 
    /**
     * Zavolani metody throwException().
     */
    public static void main(String[] args) throws Exception {
        try {
            throwException();
            System.out.println("try block");
        }
        catch (Exception e) {
            System.out.println("catch block");
        }
    }
 
}

Při spuštění tohoto příkladu se na standardním výstupu zobrazí (podle všech očekávání) jediný řádek textu:

catch block

10. Překlad demonstračního příkladu Test33.java do bajtkódu JVM

Bajtkód demonstračního příkladu Test33.java již musí obsahovat instrukce zajišťující zachycení a zpracování výjimky, proto bude velmi zajímavé ho prozkoumat. Metoda throwException() se přeloží naprosto stejným způsobem, jaký již známe z předchozího příkladu:

  public static void throwException() throws java.lang.Exception;
    Code:
       0: new           #2;                 // vytvoření instance třídy java.lang.Exception
       3: dup                               // kopie reference na nově vytvořený objekt
                                            // inicializace objektu
       4: invokespecial #3;                 // Method java/lang/Exception."<init>":()V
       7: athrow                            // objekt byl inicializován, vyhození výjimky

Ovšem bajtkód metody main() je již komplikovanější:

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
      *** blok try ***
       0: invokestatic  #4;                 // volání metody throwException:()V
                                            // příprava na volání metody System.out.println()
       3: getstatic     #5;                 // Field java/lang/System.out:Ljava/io/PrintStream;
       6: ldc           #6;                 // konstanta obsahující řetězec "try block"
       8: invokevirtual #7;                 // volání metody java.io.PrintStream.println:(java.lang.String)
      11: goto          23                  // skok ZA konec bloku catch
 
      *** blok catch ***
      14: astore_1                          // začátek bloku catch
                                            // příprava na volání metody System.out.println()
      15: getstatic     #5;                 // Field java/lang/System.out:Ljava/io/PrintStream;
      18: ldc           #8;                 // konstanta obsahujici retezec "catch block"
      20: invokevirtual #7;                 // volání metody java.io.PrintStream.println:(java.lang.String)
 
    *** konec konstrukce try-catch ***
      23: return                            // ukončení běhu metody main()
 
    Exception table:                        // tabulka obsahující seznam řádků a případných výjimek
       from    to  target type
           0    11    14   Class java/lang/Exception

V tomto bajtkódu již můžeme vidět dvě novinky. První novinka je vlastně očekávaná – první sekvence instrukcí představuje tělo bloku try, druhá sekvence instrukcí pak tělo bloku catch. Na konci těla prvního bloku je umístěna instrukce goto provádějící nepodmíněný skok za účelem přeskoku celého bloku catch. Za jakých okolností je tělo bloku catch provedeno je specifikováno v datové struktuře Exception table. Zde jsou uloženy záznamy obsahující rozsah indexů instrukcí, kde může dojít k výjimce, index instrukce, na kterou se skočí v případě skutečného vzniku výjimky a samozřejmě i typ výjimky.

11. Demonstrační příklad Test34.java: implementace struktury try-catch-finally

Dnešní poslední demonstrační příklad nazvaný Test34.java je vlastně obdobou předchozího příkladu Test33.java, ovšem namísto neúplné struktury try-catch je zde implementována úplná programová struktura try-catch-finally, přičemž příkazy umístěné v bloku finally jsou vykonány vždy, nezávisle na tom, zda k zachycované výjimce dojde či nikoli:

/**
 * Demonstracni priklad cislo 34.
 *
 * Implementace struktury try-catch-finally.
 */
public class Test34 {
 
    /**
     * Metoda, ktera vyhodi vyjimku typu Exception()
     */
    public static void throwException() throws Exception {
        throw new Exception();
    }
 
    /**
     * Zavolani metody throwException().
     */
    public static void main(String[] args) throws Exception {
        try {
            throwException();
            System.out.println("try block");
        }
        catch (Exception e) {
            System.out.println("catch block");
        }
        finally {
            System.out.println("finally block");
        }
    }
 
}

Podle očekávání dojde při spuštění tohoto příkladu k výpisu pouze dvou řetězců na standardní výstup:

catch block
finally block

12. Překlad demonstračního příkladu Test34.java do bajtkódu JVM

Opět si ukažme, jak se obě metody deklarované ve třídě Test34 přeloží do bajtkódu JVM. Začneme bajtkódem metody throwException(), protože v něm nenalezneme žádné odlišnosti od předchozího příkladu:

  public static void throwException() throws java.lang.Exception;
    Code:
       0: new           #2;                 // vytvoření instance třídy java.lang.Exception
       3: dup                               // kopie reference na nově vytvořený objekt
                                            // inicializace objektu
       4: invokespecial #3;                 // Method java/lang/Exception."<init>":()V
       7: athrow                            // objekt byl inicializován, vyhození výjimky

Bajtkód metody main() je však již velmi rozsáhlý a najdeme zde hned několik nepodmíněných skoků realizovaných instrukcí goto:

  public static void main(java.lang.String[]) throws java.lang.Exception;
    Code:
      *** blok try ***
       0: invokestatic  #4;                 // volání metody throwException:()V
                                            // příprava na volání metody System.out.println()
       3: getstatic     #5;                 // Field java/lang/System.out:Ljava/io/PrintStream;
       6: ldc           #6;                 // konstanta obsahující řetězec "try block"
       8: invokevirtual #7;                 // volání metody java.io.PrintStream.println:(java.lang.String)
 
      *** blok finally #1 ***
                                            // příprava na volání metody System.out.println()
      11: getstatic     #5;                 // Field java/lang/System.out:Ljava/io/PrintStream;
      14: ldc           #8;                 // konstanta obsahující řetězec "finally block"
      16: invokevirtual #7;                 // volání metody java.io.PrintStream.println:(java.lang.String)
      19: goto          53                  // skok ZA konec bloku finally
 
      *** blok catch ***
      22: astore_1
      23: getstatic     #5;                 // Field java/lang/System.out:Ljava/io/PrintStream;
      26: ldc           #9;                 // konstanta obsahující řetězec "catch block"
      28: invokevirtual #7;                 // volání metody java.io.PrintStream.println:(java.lang.String)
 
      *** blok finally #2 ***
      31: getstatic     #5;                 // Field java/lang/System.out:Ljava/io/PrintStream;
      34: ldc           #8;                 // konstanta obsahující řetězec "finally block"
      36: invokevirtual #7;                 // volání metody java.io.PrintStream.println:(java.lang.String)
      39: goto          53                  // skok ZA konec bloku finally
 
      *** blok finally #3 - zachycení + rethrow jiné výjimky ***
      42: astore_2                          // uložit obsah TOS do lokální proměnné
      43: getstatic     #5;                 // Field java/lang/System.out:Ljava/io/PrintStream;
      46: ldc           #8;                 // konstanta obsahující řetězec "finally block"
      48: invokevirtual #7;                 // volání metody java.io.PrintStream.println:(java.lang.String)
      51: aload_2                           // obnovit obsah TOS
      52: athrow                            // a ihned vyvolat výjimku, kterou jsme zachytili jen dočasně
 
      53: return                            // ukončení běhu metody main()
 
    Exception table:                        // tabulka obsahující seznam řádků a případných výjimek
       from    to  target type
           0    11    22   Class java/lang/Exception // blok try
           0    11    42   any                       // bloky try+finally #1
          22    31    42   any                       // blok catch
          42    43    42   any                       // instrukce zachycení jiné výjimky

Tento bajtkód sice může vypadat poněkud nepřehledně, ale ukázaný způsob překladu má své velmi dobré důvody. „Běžné“ chování metody je implementováno v instrukcích umístěných na indexech 0 až 11. Zde můžeme vidět jak implementaci všech příkazů v bloku try, tak i implementaci všech příkazů v bloku finally. To je vlastně logické, protože když nedojde ke vzniku výjimky, vykonají se právě tyto dva bloky v uvedeném pořadí (napřed blok try, poté blok finally).

CS24_early

V tabulce výjimek můžeme vidět, že instrukce od indexu 0 do indexu 11 jsou zde zmíněny hned dvakrát – pokud zde dojde k výjimce Exception (kterou zachytáváme), provedou se bloky catch+finally #2, které začínají na indexu 22. To je také očekávané chování, které je explicitně zapsáno ve zdrojovém kódu. Zajímavější je předposlední blok finally vykonaný ve chvíli, kdy vznikne jiný typ výjimky. I v tomto případě je totiž nutné vypsat řetězec „finally block“, ale navíc musí neznámá výjimka „probublat“ do volající metody, a to již známým způsobem: s využitím instrukce athrow.

13. Repositář se zdrojovými kódy všech sedmi dnešních demonstračních příkladů

Všech sedm (3+4) dnes popsaných a „disasemblovaných“ demonstračních příkladů bylo uloženo do Mercurial repositáře umístěného na adrese http://icedtea.classpath.or­g/people/ptisnovs/jvm-tools/ (revize s kódy 320255fc8ed7673d8257fbd1). Odkazy na prozatím poslední verze těchto sedmi příkladů naleznete v tabulce umístěné pod tímto odstavcem:

14. Odkazy na Internetu

  1. Python Byte Code Instructions
    https://docs.python.org/re­lease/2.5.2/lib/bytecodes­.html
  2. Python 2.x: funkce range()
    https://docs.python.org/2/li­brary/functions.html#range
  3. Python 2.x: typ iterátor
    https://docs.python.org/2/li­brary/stdtypes.html#itera­tor-types
  4. Python break, continue and pass Statements
    http://www.tutorialspoint­.com/python/python_loop_con­trol.htm
  5. Python Bytecode: Fun With Dis
    http://akaptur.github.io/blog/2013/08/14/pyt­hon-bytecode-fun-with-dis/
  6. Python's Innards: Hello, ceval.c!
    http://tech.blog.aknin.na­me/category/my-projects/pythons-innards/
  7. Byterun
    https://github.com/nedbat/byterun
  8. Python Byte Code Instructions
    http://document.ihg.uni-duisburg.de/Documentation/Pyt­hon/lib/node56.html
  9. Python Byte Code Instructions
    https://docs.python.org/3­.2/library/dis.html#python-bytecode-instructions
  10. dis – Python module
    https://docs.python.org/2/li­brary/dis.html
  11. Comparison of Python virtual machines
    http://polishlinux.org/ap­ps/cli/comparison-of-python-virtual-machines/
  12. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  13. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  14. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
  15. Programming in Lua 9.1 – Coroutine Basics,
    http://www.lua.org/pil/9.1.html
  16. Wikipedia CZ: Koprogram,
    http://cs.wikipedia.org/wi­ki/Koprogram
  17. Wikipedia EN: Coroutine,
    http://en.wikipedia.org/wi­ki/Coroutine
  18. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  19. Programming in Lua: 6 – More about Functions
    http://www.lua.org/pil/6.html
  20. Lua Lanes,
    http://kotisivu.dnainternet­.net/askok/bin/lanes/
  21. Programming in Lua: 6.1 – Closures
    http://www.lua.org/pil/6.1.html
  22. Programming in Lua: 9.1 – Coroutine Basics
    http://www.lua.org/pil/9.1.html
  23. Programming in Lua: Numeric for
    http://www.lua.org/pil/4.3.4.html
  24. Programming in Lua: break and return
    http://www.lua.org/pil/4.4.html
  25. Programming in Lua: Tables
    http://www.lua.org/pil/2.5.html
  26. Programming in Lua: Table Constructors
    http://www.lua.org/pil/3.6.html
  27. Programovací jazyk Lua
    http://palmknihy.cz/web/kni­ha/programovaci-jazyk-lua-12651.htm
  28. Lua: Tables Tutorial
    http://lua-users.org/wiki/TablesTutorial
  29. Lua: Control Structure Tutorial
    http://lua-users.org/wiki/ControlStruc­tureTutorial
  30. Lua Types Tutorial
    http://lua-users.org/wiki/LuaTypesTutorial
  31. Goto Statement in Lua
    http://lua-users.org/wiki/GotoStatement
  32. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  33. Lua 5.2 sources – lopcodes.h
    http://www.lua.org/source/5­.2/lopcodes.h.html
  34. Lua 5.2 sources – lopcodes.c
    http://www.lua.org/source/5­.2/lopcodes.c.html
  35. For-each Loop in Java
    http://www.leepoint.net/notes-java/flow/loops/foreach.html
  36. For Loop (Wikipedia)
    http://en.wikipedia.org/wiki/For_loop
  37. Heinz Rutishauser
    http://en.wikipedia.org/wi­ki/Heinz_Rutishauser
  38. Parrot
    http://www.parrot.org/
  39. Parrot languages
    http://www.parrot.org/languages
  40. Parrot Primer
    http://docs.parrot.org/pa­rrot/latest/html/docs/intro­.pod.html
  41. Parrot Opcodes
    http://docs.parrot.org/pa­rrot/latest/html/ops.html
  42. Parrot VM
    http://en.wikibooks.org/wi­ki/Parrot_Virtual_Machine
  43. Parrot Assembly Language
    http://www.perl6.org/archi­ve/pdd/pdd06_pasm.html
  44. Parrot Reference: Chapter 11 – Perl 6 and Parrot Essentials
    http://oreilly.com/perl/excerpts/perl-6-and-parrot-essentials/parrot-reference.html
  45. O-code
    http://en.wikipedia.org/wiki/O-code_machine
  46. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  47. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  48. GC safe-point (or safepoint) and safe-region
    http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html
  49. Safepoints in HotSpot JVM
    http://blog.ragozin.info/2012/10/sa­fepoints-in-hotspot-jvm.html
  50. Java theory and practice: Synchronization optimizations in Mustang
    http://www.ibm.com/develo­perworks/java/library/j-jtp10185/
  51. How to build hsdis
    http://hg.openjdk.java.net/jdk7/hot­spot/hotspot/file/tip/src/sha­re/tools/hsdis/README
  52. Java SE 6 Performance White Paper
    http://www.oracle.com/technet­work/java/6-performance-137236.html
  53. Lukas Stadler's Blog
    http://classparser.blogspot­.cz/2010/03/hsdis-i386dll.html
  54. How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
    http://dropzone.nfshost.com/hsdis.htm
  55. PrintAssembly
    https://wikis.oracle.com/dis­play/HotSpotInternals/Prin­tAssembly
  56. The Java Virtual Machine Specification: 3.14. Synchronization
    http://docs.oracle.com/ja­vase/specs/jvms/se7/html/jvms-3.html#jvms-3.14
  57. The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4
  58. The Java Virtual Machine Specification: 17.4. Memory Model
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.4
  59. The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
    http://docs.oracle.com/ja­vase/specs/jls/se7/html/jls-17.html#jls-17.7
  60. Open Source ByteCode Libraries in Java
    http://java-source.net/open-source/bytecode-libraries
  61. ASM Home page
    http://asm.ow2.org/
  62. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  63. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  64. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  65. BCEL Home page
    http://commons.apache.org/bcel/
  66. Byte Code Engineering Library (před verzí 5.0)
    http://bcel.sourceforge.net/
  67. Byte Code Engineering Library (verze >= 5.0)
    http://commons.apache.org/pro­per/commons-bcel/
  68. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  69. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  70. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  71. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  72. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  73. Javassist
    http://www.jboss.org/javassist/
  74. Byteman
    http://www.jboss.org/byteman
  75. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  76. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  77. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  78. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  79. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  80. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  81. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  82. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  83. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  84. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  85. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  86. Cobertura
    http://cobertura.sourceforge.net/
  87. jclasslib bytecode viewer
    http://www.ej-technologies.com/products/jclas­slib/overview.html

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.