Hlavní navigace

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

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

Sdílet

V předchozí části tohoto seriálu jsme si podrobně popsali, jakým způsobem je realizováno zpracování výjimek v bajtkódu JVM. Dnes tuto poměrně rozsáhlou problematiku dokončíme, protože si ukážeme způsob práce s výjimkami v programech, které jsou překládány do bajtkódů Lua VM a především pak Python VM.

Obsah

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

2. Zpracování výjimek v jazyku Lua

3. Demonstrační příklad Test33.lua: vyhození výjimky v jazyku Lua

4. Překlad demonstračního příkladu Test33.lua do bajtkódu Lua VM

5. Demonstrační příklad Test34.lua: vyhození a následné zachycení výjimky v jazyku Lua

6. Překlad demonstračního příkladu Test34.lua do bajtkódu Lua VM

7. Zpracování výjimek v jazyku Python

8. Demonstrační příklad Test37.py: vyhození výjimky v jazyku Python

9. Překlad demonstračního příkladu Test37.py do bajtkódu Python VM

10. Demonstrační příklad Test38.py: vyhození a následné zachycení výjimky v jazyku Python

11. Překlad demonstračního příkladu Test38.py do bajtkódu Python VM

12. Demonstrační příklad Test39.py: implementace struktury try-catch-finally v jazyku Python

13. Překlad demonstračního příkladu Test39.py do bajtkódu Python VM

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

15. Odkazy na Internetu

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

V předchozí části seriálu o JVM i o dalších virtuálních strojích jsme si na několika demonstračních příkladech ukázali, jakým způsobem se pracuje s výjimkami v bajtkódu virtuálního stroje Javy (JVM). Připomeňme si, že v JVM slouží pro vytvoření výjimky (přesněji řečeno objektu představujícího výjimku) instrukce new, která výjimku vytvoří a taktéž uloží referenci na tuto výjimku na zásobník operandů (TOS – Top of Stack). Samotné vyhození výjimky je provedeno instrukcí athrow. Tato instrukce očekává, že na vrcholu zásobníku operandů bude uložena reference na výjimku. Metoda, ze které může být vyhozena výjimka, musí ve svých metadatech obsahovat informaci o typu vyhazované výjimky či o typech výjimek (pokud jich je větší počet). Toto pravidlo kontrolované jak překladačem, tak i classloaderem nemusí být dodrženo pro běhové výjimky, tj. pro všechny třídy přímo či nepřímo odvozené od třídy RuntimeException.

Zachycení výjimek je ovšem poněkud složitější. Je řešeno přímo v bajtkódu (explicitní přeskakování kódu náležejícímu bloku catch, dvojitá implementace bloku finally atd.) a taktéž tabulkou přiřazenou k metodě, v níž se má výjimka zachytit. V této tabulce jsou uloženy rozsahy indexů instrukcí, v nichž může k výjimce dojít, index instrukce, na níž se provede skok při vzniku výjimky a taktéž typ výjimky (což je odkaz do constant poolu). Podívejme se na jednoduchý příklad takové tabulky. Poznámky byly samozřejmě dopsány ručně:

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

2. Zpracování výjimek v jazyku Lua

Minule jsme se taktéž alespoň ve stručnosti zmínili o způsobu práce s výjimkami v programovacím jazyce Lua. Zde se – což je v moderních „mainstreamových“ programovacích jazycích dosti neobvyklé – pro zachycení výjimky nepoužívají programové bloky typu try-catch či try-catch-finally, ale dvojice funkcí nazvaných pcall()error(). Funkce error() slouží k vyhození výjimky, což znamená, že má podobný význam jako příkaz throw v programovacím jazyku Java či podobný příkaz raise, který najdeme v programovacím jazyku Python. Funkce error() může být ve zdrojovém kódu použita kdekoli; není tedy zapotřebí žádné zvláštní deklarace, která by označovala ty části kódu, kde je tato funkce použita.

Naproti tomu funkce pcall() slouží k zavolání jiné (libovolné) funkce v takzvaném „sandboxu“ (pcall = protected call). Pokud nevznikne ve volané funkci žádná výjimka, vrací pcall() ve svém prvním výsledku hodnotu true, v opačném případě (vznik výjimky) je vrácena hodnota false. Ve skutečnosti však funkce pcall() může vracet větší množství hodnot, přičemž další hodnoty (kromě hodnoty první) jsou výsledkem funkce předané do pcall(). Při vzniku výjimky je vrácena jen dvojice false,výjimka (samotná výjimka je typicky představována řetězcem, není to však nikde předepsáno).

Ukažme si celou strukturu vyvolávání a zachycování výjimek na velmi jednoduchém příkladu:

function noException1()
    return 1
end
 
function noException2()
    return 1,2
end
 
function noException3()
    return 1,2,3
end
 
function throwException1()
    error(1)
end
 
function throwException2()
    error("Exception!!!")
end
 
function main()
    print("Status", "Return value(s)")
    print(pcall(noException1))
    print(pcall(noException2))
    print(pcall(noException3))
    print(pcall(throwException1))
    print(pcall(throwException2))
end
 
main()

Výsledek vypsaný na standardní výstup:

Status  Return value(s)
true    1
true    1       2
true    1       2       3
false   Test.lua:14: 1
false   Test.lua:18: Exception!!!

3. Demonstrační příklad Test33.lua: vyhození výjimky v jazyku Lua

Nyní se již můžeme podívat na způsob překladu dvojice funkcí pcall()error() do bajtkódu programovacího jazyka Lua. Nejprve si ukážeme velmi jednoduchý demonstrační příklad, v němž je funkce error() volána s libovolným parametrem – hodnota tohoto parametru bude představovat výjimku. Jak jsme si již řekli v předchozí kapitole, může výjimku představovat jakákoli hodnota, typicky řetězec, ale může se jednat i o tabulku, celé či desetinné číslo atd.:

--
-- Demonstracni priklad cislo 33.
--
-- Vyhozeni vyjimky s vyuzitim funkce error().
--
 
 
 
--
-- Funkce, ktera vyhodi vyjimku.
--
function throwNewException(message)
    error(message)
end
 
 
 
--
-- Spusteni testu.
--
function main()
    print("Calling function 'throwNewException()'")
    throwNewException("Hello world!")
    print("Returned from function 'throwNewException()'")
end
 
 
 
main()
 
 
 
--
-- Finito.
--

Výjimka, která je vyhozena funkcí error(), není nikde zachycena, takže nás pravděpodobně nepřekvapí, že demonstrační příklad Test33.lua vypíše na terminál následující zprávy:

lua: Test33.lua:13: Hello world!
stack traceback:
        [C]: in function 'error'
        Test33.lua:13: in function 'throwNewException'
        Test33.lua:23: in function 'main'
        Test33.lua:29: in main chunk
        [C]: ?
Calling function 'throwNewException()'

Jedná se o klasický výpis stack trace, v němž jsou navíc naznačeny nativní funkce pomocí řetězce [C].

4. Překlad demonstračního příkladu Test33.lua do bajtkódu Lua VM

Při pohledu na bajtkód demonstračního příkladu Test33.lua je patrné, že se v něm ve skutečnosti nenachází žádné speciální instrukce ani metadata, která by souvisela s vyhozením výjimky! Můžeme zde vidět pouze volání funkce error(). To, že se jedná o nativní funkci (naprogramovanou v céčku), z bajtkódu nevyčteme; to je již implementační záležitost virtuálního stroje:

throwNewException():
function <Test33.lua:12,14> (4 instructions, 16 bytes at 0x460f60)
1 param, 3 slots, 0 upvalues, 1 local, 1 constant, 0 functions
        1       [13]    GETGLOBAL       1 -1    ; získání reference na funkci error()
        2       [13]    MOVE            2 0     ; parametr předaný funkci error
        3       [13]    CALL            1 2 1   ; zavolání funkce error (registr R1) s předáním parametru v registru R2
 
        4       [14]    RETURN          0 1     ; návrat z funkce throwNewException

Ani volání funkce nazvané throwNewException není v žádném ohledu odlišné od volání jiných funkcí:

main():
function <Test33.lua:21,25> (10 instructions, 40 bytes at 0x461240)
0 params, 2 slots, 0 upvalues, 0 locals, 5 constants, 0 functions
        1       [22]    GETGLOBAL       0 -1    ; získání reference na funkci print
        2       [22]    LOADK           1 -2    ; řetězec "Calling function 'throwNewException'"
        3       [22]    CALL            0 2 1   ; zavolání funkce print() s předáním parametru v registru R1
 
        4       [23]    GETGLOBAL       0 -3    ; získání reference na funkci throwNewException
        5       [23]    LOADK           1 -4    ; řetězec "Hello world!"
        6       [23]    CALL            0 2 1   ; zavolat funkci throwNewException
 
        7       [24]    GETGLOBAL       0 -1    ; získání reference na funkci print
        8       [24]    LOADK           1 -5    ; řetězec "Returned from function 'throwNewException()'"
        9       [24]    CALL            0 2 1   ; zavolání funkce print s předáním parametru v registru R1
 
        10      [25]    RETURN          0 1     ; návrat z funkce main

5. Demonstrační příklad Test34.lua: vyhození a následné zachycení výjimky v jazyku Lua

Dnešní druhý demonstrační příklad nazvaný Test34.lua je již poněkud složitější než příklad předchozí, a to z toho důvodu, že se zde funkce throwNewException nevolá přímo. Namísto přímého volání se používá „sandbox“ vytvořený funkcí pcall (protected call). Povšimněte si způsobu použití této funkce – namísto throwNewException(„Hello world!“) se použije volání pcall(throwNewException, "Hello world!), tj. volaná funkce je zde předávána jako parametr – funkce jsou totiž v programovacím jazyku Lua plnohodnotnými datovými typy. Funkce pcall() vrací v prvním výsledku stav volání „sandboxované“ funkce:

--
-- Demonstracni priklad cislo 34.
--
-- Vyhozeni vyjimky s vyuzitim funkce error()
-- s naslednym zachycenim teze vyjimky funkci pcall().
--
 
 
 
--
-- Funkce, ktera vyhodi vyjimku.
--
function throwNewException(message)
    error(message)
end
 
 
 
--
-- Spusteni testu.
--
function main()
    print("Calling function 'throwNewException()'")
    local status, message = pcall(throwNewException, "Hello world!")
    print("Returned from function 'throwNewException()'")
    print("    Status  = ", status)
    print("    Message = ", message)
end
 
 
 
main()
 
 
 
--
-- Finito.
--

Po spuštění tohoto demonstračního příkladu se na standardní výstup vypíšou tyto zprávy oznamující, že při spuštění funkce throwNewException skutečně došlo k vyhození výjimky:

Calling function 'throwNewException()'
Returned from function 'throwNewException()'
    Status  =   false
    Message =   Test34.lua:14: Hello world!

6. Překlad demonstračního příkladu Test34.lua do bajtkódu Lua VM

Bajtkód funkce throwNewException() se (podle očekávání) nijak neliší od bajtkódu stejnojmenné funkce, kterou jsme již implementovali v předchozím demonstračním příkladu, o čemž se můžeme snadno přesvědčit:

throwNewException():
function <Test34.lua:13,15> (4 instructions, 16 bytes at 0x175af60)
1 param, 3 slots, 0 upvalues, 1 local, 1 constant, 0 functions
        1       [14]    GETGLOBAL       1 -1    ; získání reference na funkci error()
        2       [14]    MOVE            2 0     ; parametr předaný funkci error
        3       [14]    CALL            1 2 1   ; zavolání funkce error (registr R1) s předáním parametru v registru R2
 
        4       [15]    RETURN          0 1     ; návrat z funkce throwNewException

Komplikovanější je však bajtkód funkce main, neboť zde je nutné implementovat volání pcall(throwNewException, message), tj. funkce throwNewException zde vystupuje jako parametr funkce pcall. Povšimněte si, jak vysokoúrovňový vlastně bajtkód Lua VM je, zejména v porovnání s JVM:

main():
function <Test34.lua:22,28> (19 instructions, 76 bytes at 0x175b240)
0 params, 5 slots, 0 upvalues, 2 locals, 8 constants, 0 functions
        1       [23]    GETGLOBAL       0 -1    ; získání reference na funkci print
        2       [23]    LOADK           1 -2    ; řetězec "Calling function 'throwNewException'"
        3       [23]    CALL            0 2 1   ; zavolání funkce print s předáním parametru v registru R1
 
        4       [24]    GETGLOBAL       0 -3    ; získání reference na funkci pcall
        5       [24]    GETGLOBAL       1 -4    ; získání reference na funkci throwNewException
        6       [24]    LOADK           2 -5    ; řetězec "Hello world!"
        7       [24]    CALL            0 3 3   ; zavolání funkce pcall s dvojicí parametrů (throwNewException, "Hello world!"
 
        8       [25]    GETGLOBAL       2 -1    ; získání reference na funkci print
        9       [25]    LOADK           3 -6    ; řetězec "Returned from function 'throwNewException()'"
        10      [25]    CALL            2 2 1   ; zavolání funkce print
 
        11      [26]    GETGLOBAL       2 -1    ; získání reference na funkci print
        12      [26]    LOADK           3 -7    ; řetězec "    Status  = "
        13      [26]    MOVE            4 0     ; první návratová hodnota funkce pcall
        14      [26]    CALL            2 3 1   ; zavolání funkce print se dvěma parametry (registry R3 a R4)
 
        15      [27]    GETGLOBAL       2 -1    ; získání reference na funkci print
        16      [27]    LOADK           3 -8    ; řetězec "    Message = "
        17      [27]    MOVE            4 1     ; druhá návratová hodnota funkce pcall
        18      [27]    CALL            2 3 1   ; zavolání funkce print se dvěma parametry (registry R3 a R4)
 
        19      [28]    RETURN          0 1     ; návrat z funkce main

7. Zpracování výjimek v jazyku Python

Způsob práce s výjimkami v programovacím jazyce Python se ze syntaktického i sémantického hlediska do značné míry podobá způsobu zpracování výjimek v programovacím jazyce Java, samozřejmě s ohledem na to, že Python používá dynamické vyhodnocování datových typů a nevyžaduje striktně explicitní zachycení výjimek. Nepatrné rozdíly najdeme i v syntaxi zápisu, protože se namísto klíčového slova catch používá slovo except. Větší rozdíly však najdeme na úrovni bajtkódu, neboť v Pythonu se nepoužívají speciální tabulky s informacemi o tom, v jakém místě kódu mohou nastat výjimky (tyto tabulky najdeme jen v JVM). Namísto toho se používají takzvané bloky instrukcí, přičemž informace o těchto blocích jsou ukládány na zásobníkový rámec. Bloky se vytváří a ruší dynamicky za běhu aplikace, a to s využitím specializovaných instrukcí SETUP_EXCEPT, SETUP_FINALLY, POP_BLOCKEND_FINALLY.

8. Demonstrační příklad Test37.py: vyhození výjimky v jazyku Python

Dnešní první demonstrační příklad ukazující způsob zpracování výjimek v Pythonu nese název Test37.py. V tomto velmi jednoduchém příkladu se ve funkci throwNewException vyhodí nová výjimka typu Exception. Při konstrukci objektu představujícího výjimku se použije zpráva předaná do funkce throwNewException:

#
# Demonstracni priklad cislo 37.
#
# Vyhozeni vyjimky.
#
 
 
 
#
# Funkce, ktera vyhodi vyjimku.
#
def throwNewException(message):
    raise Exception(message)
 
 
 
#
# Spusteni testu.
#
def main():
    print("Calling function 'throwNewException()'")
    throwNewException("Hello world!")
    print("Returned from function 'throwNewException()'")
 
 
 
#
# Ukazka disasembleru.
# (prekladu funkci do bajtkodu Python VM).
#
def disassemble():
    from dis import dis
 
    print("\nthrowNewException():")
    dis(throwNewException)
 
    print("\nmain():")
    dis(main)
 
 
 
main()
 
#disassemble()
 
#
# Finito.
#

Po spuštění tohoto demonstračního příkladu vznikne (dle očekávání) výjimka, která není zachycena. Z tohoto důvodu dojde k ukončení běhu aplikace a k vypsání historie volání funkcí:

Calling function 'throwNewException()'
Traceback (most recent call last):
  File "Test37.py", line 42, in
    main()
  File "Test37.py", line 22, in main
    throwNewException("Hello world!")
  File "Test37.py", line 13, in throwNewException
    raise Exception(message)
Exception: Hello world!

9. Překlad demonstračního příkladu Test37.py do bajtkódu Python VM

Překlad funkce throwNewException do bajtkódu Python VM ukazuje, jakým způsobem je přeložen příkaz raise. Využívá se zde instrukce RAISE_VARARGS, které se předá aktuální počet parametrů od 0 do 3. V našem případě se předává jen jediný parametr – vlastní výjimka:

throwNewException():
 13           0 LOAD_GLOBAL              0 (Exception) ; příprava na vytvoření objektu představujícího výjimku
                                                       ; získání reference třídy, jejíž instance se bude vytvářet
              3 LOAD_FAST                0 (message)   ; příprava na vytvoření objektu představujícího výjimku
                                                       ; načtení parametru funkce
              6 CALL_FUNCTION            1             ; volání Exception(message) - vytvoření výjimky
 
              9 RAISE_VARARGS            1             ; vlastní vyhození výjimky
 
             12 LOAD_CONST               0 (None)      ; návratová hodnota funkce throwNewException
             15 RETURN_VALUE                           ; výskok z funkce throwNewException

Vzhledem k tomu, že se výjimka z funkce throwNewException nikde nezachycuje, je bajtkód funkce main velmi jednoduchý a vcelku přímočaře odpovídá zdrojovému kódu:

main():
 21           0 LOAD_CONST               1             ; řetězec "Calling function 'throwNewException()'")
              3 PRINT_ITEM                             ; tisk řetězce na standardní výstup
              4 PRINT_NEWLINE                          ; odřádkování
 
 22           5 LOAD_GLOBAL              0 (throwNewException)
                                                       ; získání reference funkce, která se bude volat
              8 LOAD_CONST               2             ; řetězec 'Hello world!'
             11 CALL_FUNCTION            1             ; zavolání funkce throwNewException
             14 POP_TOP                                ; úklid návratové hodnoty (nevyužijeme ji)
 
 23          15 LOAD_CONST               3             ; řetězec "Returned from function 'throwNewException()'"
             18 PRINT_ITEM                             ; tisk řetězce na standardní výstup
             19 PRINT_NEWLINE                          ; odřádkování
 
             20 LOAD_CONST               0 (None)      ; návratová hodnota funkce main
             23 RETURN_VALUE                           ; výskok z funkce main

10. Demonstrační příklad Test38.py: vyhození a následné zachycení výjimky v jazyku Python

Ve druhém demonstračním příkladu naprogramovaném v Pythonu se již výjimka vyhazovaná z funkce throwNewException zachycuje, a to konkrétně v bloku catch. Zdrojový kód tohoto demonstračního příkladu je opět velmi jednoduchý:

#
# Demonstracni priklad cislo 38.
#
# Vyhozeni vyjimky s jejim naslednym zachycenim.
#
 
 
 
#
# Funkce, ktera vyhodi vyjimku.
#
def throwNewException(message):
    raise Exception(message)
 
 
 
#
# Spusteni testu.
#
def main():
    try:
        print("Calling function 'throwNewException()'")
        throwNewException("Hello world!")
        print("Returned from function 'throwNewException()'")
    except:
        print("Catch/except block")
 
 
 
#
# Ukazka disasembleru.
# (prekladu funkci do bajtkodu Python VM).
#
def disassemble():
    from dis import dis
 
    print("\nthrowNewException():")
    dis(throwNewException)
 
    print("\nmain():")
    dis(main)
 
 
 
main()
 
#disassemble()
 
#
# Finito.
#

Po spuštění se na standardní výstup vypíšou pouze dva řádky textu, což znamená, že příkaz print(„Returned from function ‚throwNewException()‘“) byl přeskočen:

Calling function 'throwNewException()'
Catch/except block

11. Překlad demonstračního příkladu Test38.py do bajtkódu Python VM

Opět se podívejme na bajtkód demonstračního příkladu Test38.py.

Překlad funkce throwNewException do bajtkódu Python VM je zcela stejný, jako tomu bylo v předchozím demonstračním příkladu:

throwNewException():
 13           0 LOAD_GLOBAL              0 (Exception) ; příprava na vytvoření objektu představujícího výjimku
                                                       ; získání reference třídy, jejíž instance se bude vytvářet
              3 LOAD_FAST                0 (message)   ; příprava na vytvoření objektu představujícího výjimku
                                                       ; načtení parametru funkce
              6 CALL_FUNCTION            1             ; volání Exception(message) - vytvoření výjimky
 
              9 RAISE_VARARGS            1             ; vlastní vyhození výjimky
 
             12 LOAD_CONST               0 (None)      ; návratová hodnota funkce throwNewException
             15 RETURN_VALUE                           ; výskok z funkce throwNewException

Komplikovanější je však bajtkód funkce main. Zde se můžeme setkat s několika novými instrukcemi, zejména s trojicí instrukcí SETUP_EXCEPT, POP_BLOCKEND_FINALLY. První z těchto instrukcí slouží k vytvoření bloku (uvnitř zásobníkového rámce), v němž může dojít k výjimce. Tento blok se odstraňuje instrukcí POP_BLOCK a poslední zmíněná instrukce END_FINALLY se používá v bloku finally (i když je prázdný). Za zmínku taktéž stojí okomentovaná trojice instrukcí POP_TOP:

main():
 21           0 SETUP_EXCEPT            24 (to 27)     ; vytvoření bloku instrukcí, v nichž může dojít k výjimce
                                                       ; 27 je index první instrukce v bloku except
 
začátek bloku *try*
 
 22           3 LOAD_CONST               1             ; řetězec "Calling function 'throwNewException()'")
              6 PRINT_ITEM                             ; tisk řetězce na standardní výstup
              7 PRINT_NEWLINE                          ; odřádkování
 
 23           8 LOAD_GLOBAL              0 (throwNewException)
                                                       ; získání reference funkce, která se bude volat
             11 LOAD_CONST               2             ; řetězec 'Hello world!'
             14 CALL_FUNCTION            1             ; zavolání funkce throwNewException
             17 POP_TOP                                ; úklid návratové hodnoty (nevyužijeme ji)
 
 24          18 LOAD_CONST               3             ; řetězec "Returned from function 'throwNewException()'"
             21 PRINT_ITEM                             ; tisk řetězce na standardní výstup
             22 PRINT_NEWLINE                          ; odřádkování
 
             23 POP_BLOCK                              ; volání proběhlo v pořádku - zrušíme celý blok
                                                       ; vytvořený přes SETUP_EXCEPT
             24 JUMP_FORWARD            12 (to 39)     ; skok na konec funkce (přeskok dalšího bloku)
 
konec bloku *try*
 
začátek bloku *except*
 
 25          27 POP_TOP                                ; úklid na zásobníku operandů (vlastní výjimka)
             28 POP_TOP                                ; úklid na zásobníku operandů (parametr výjimky)
             29 POP_TOP                                ; úklid na zásobníku operandů (handler)
 
 26          30 LOAD_CONST               4             ; řetězec "Catch/except block"
             33 PRINT_ITEM                             ; tisk řetězce na standardní výstup
             34 PRINT_NEWLINE                          ; odřádkování
 
             35 JUMP_FORWARD             1 (to 39)     ; skok na konec funkce (přeskok dalšího prázdného bloku)
 
konec bloku *except*
 
začátek bloku *finally*
 
             38 END_FINALLY                            ; ukončení bloku finally
 
konec bloku *finally*
 
             39 LOAD_CONST               0 (None)      ; návratová hodnota funkce main
             42 RETURN_VALUE                           ; výskok z funkce main

12. Demonstrační příklad Test39.py: implementace struktury try-catch-finally v jazyku Python

V dnešním posledním demonstračním příkladu je již implementována úplná řídicí struktura try-catch-finally, resp. přesněji řečeno try-except-finally, pokud se budeme držet názvosloví používaného v programovacím jazyku Python:

#
# Demonstracni priklad cislo 39.
#
# Implementace struktury try/catch/finally v Pythonu.
#
 
 
 
#
# Funkce, ktera vyhodi vyjimku.
#
def throwNewException(message):
    raise Exception(message)
 
 
 
#
# Spusteni testu.
#
def main():
    try:
        print("Calling function 'throwNewException()'")
        throwNewException("Hello world!")
        print("Returned from function 'throwNewException()'")
    except:
        print("Catch/except block")
    finally:
        print("Finally block")
 
 
 
#
# Ukazka disasembleru.
# (prekladu funkci do bajtkodu Python VM).
#
def disassemble():
    from dis import dis
 
    print("\nthrowNewException():")
    dis(throwNewException)
 
    print("\nmain():")
    dis(main)
 
 
 
main()
 
#disassemble()
 
#
# Finito.
#

Po spuštění tohoto příkladu se na standardním výstupu objeví následující trojice řádků:

Calling function 'throwNewException()'
Catch/except block
Finally block

13. Překlad demonstračního příkladu Test39.py do bajtkódu Python VM

Překlad funkce throwNewException do bajtkódu Python VM je opět zcela stejný, jako tomu bylo v minulém i předminulém demonstračním příkladu:

ict ve školství 24

throwNewException():
 13           0 LOAD_GLOBAL              0 (Exception) ; příprava na vytvoření objektu představujícího výjimku
                                                       ; získání reference třídy, jejíž instance se bude vytvářet
              3 LOAD_FAST                0 (message)   ; příprava na vytvoření objektu představujícího výjimku
                                                       ; načtení parametru funkce
              6 CALL_FUNCTION            1             ; volání Exception(message) - vytvoření výjimky
 
              9 RAISE_VARARGS            1             ; vlastní vyhození výjimky
 
             12 LOAD_CONST               0 (None)      ; návratová hodnota funkce throwNewException
             15 RETURN_VALUE                           ; výskok z funkce throwNewException

Bajtkód funkce main se opět stal složitějším, a to zejména kvůli implementaci řídicí struktury finally. Taktéž zde najdeme novou instrukci SETUP_FINALLY, která dynamicky vytváří na zásobníkovém rámci blok instrukcí odpovídajících programovým řádkům od klíčového slova try až po finally:

main():
 21           0 SETUP_FINALLY           43 (to 46)     ; vytvoření bloku instrukcí, v nichž jsou implementovány bloky try-finally
              3 SETUP_EXCEPT            24 (to 30)     ; vytvoření bloku instrukcí, v nichž může dojít k výjimce
 
začátek bloku *try*
 
 22           9 LOAD_CONST               1             ; řetězec "Calling function 'throwNewException()'")
              9 PRINT_ITEM                             ; tisk řetězce na standardní výstup
             10 PRINT_NEWLINE                          ; odřádkování
 
 23          11 LOAD_GLOBAL              0 (throwNewException)
                                                       ; získání reference funkce, která se bude volat
             14 LOAD_CONST               2             ; řetězec 'Hello world!'
             17 CALL_FUNCTION            1             ; zavolání funkce throwNewException
             20 POP_TOP                                ; úklid návratové hodnoty (nevyužijeme ji)
 
 24          21 LOAD_CONST               3             ; řetězec "Returned from function 'throwNewException()'"
             24 PRINT_ITEM                             ; tisk řetězce na standardní výstup
             25 PRINT_NEWLINE                          ; odřádkování
 
             26 POP_BLOCK                              ; volání proběhlo v pořádku - zrušíme celý blok
                                                       ; vytvořený přes SETUP_EXCEPT
             27 JUMP_FORWARD            12 (to 42)     ; skok na konec funkce (přeskok dalšího bloku)
 
konec bloku *try*
 
začátek bloku *except*
 
 25          30 POP_TOP                                ; úklid na zásobníku operandů (vlastní výjimka)
             31 POP_TOP                                ; úklid na zásobníku operandů (parametr výjimky)
             32 POP_TOP                                ; úklid na zásobníku operandů (handler)
 
 26          33 LOAD_CONST               4             ; řetězec "Catch/except block"
             36 PRINT_ITEM                             ; tisk řetězce na standardní výstup
             37 PRINT_NEWLINE                          ; odřádkování
             38 JUMP_FORWARD             1 (to 42)     ; skok na konec funkce (přeskok dalšího bloku)
 
konec bloku *except*
 
             41 END_FINALLY                            ; ukončení prvního bloku finally
             42 POP_BLOCK                              ; náhrada návratové hodnoty funkce
             43 LOAD_CONST               0 (None)      ; náhrada návratové hodnoty funkce
 
začátek bloku *finally*
 
 28          46 LOAD_CONST               5             ; řetězec "('Finally block')"
             49 PRINT_ITEM                             ; tisk řetězce na standardní výstup
             50 PRINT_NEWLINE                          ; odřádkování
 
             51 END_FINALLY                            ; ukončení bloku finally
 
konec bloku *finally*
 
             52 LOAD_CONST               0 (None)      ; návratová hodnota funkce main
             55 RETURN_VALUE                           ; výskok z funkce main

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

Všech pět 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 8dc74f8e40f150d36e484ab0). Odkazy na prozatím poslední verze těchto pěti příkladů naleznete v tabulce umístěné pod tímto odstavcem:

15. 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

Autor článku

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