Obsah
1. Pohled pod kapotu JVM – volání funkcí a použití generátorů i uzávěrů v Python VM
2. Demonstrační příklad Test28.py: volání funkcí v Pythonu
3. Překlad demonstračního příkladu Test28.py do bajtkódu Python VM
4. Demonstrační příklad Test29.py: volání funkcí v Pythonu
5. Překlad demonstračního příkladu Test29.py do bajtkódu Python VM
6. Demonstrační příklad Test30.py: funkce s pojmenovanými parametry
7. Překlad demonstračního příkladu Test30.py do bajtkódu Python VM
8. Demonstrační příklad Test31.py: parametry s implicitní hodnotou
9. Překlad demonstračního příkladu Test31.py do bajtkódu Python VM
10. závěry a generátory v Pythonu
11. Repositář se zdrojovými kódy všech šesti dnešních demonstračních příkladů
1. Pohled pod kapotu JVM – volání funkcí a použití generátorů i uzávěrů v Python VM
V předchozích třech částech seriálu o programovacím jazyku Java i o virtuálním stroji Javy jsme si vysvětlili způsob volání metod, funkcí i konstruktorů v JVM (přesněji řečeno v bajtkódu JVM) a taktéž v bajtkódu Lua VM. Zde již byla popisovaná problematika poněkud zajímavější, a to především z toho důvodu, že programovací jazyk Lua plně podporuje použití takzvaných uzávěrů (closures) (pro jejich konstrukci jsou určeny zvláštní instrukce v bajtkódu). Dnes si popíšeme způsob volání funkcí v bajtkódu virtuálního stroje Pythonu. Kromě volání „běžných“ funkcí si popíšeme i práci s generátory, pro jejichž implementaci je (kupodivu) v Pythonu vyhrazeno zvláštní klíčové slovo yield (v Lue se naproti tomu podobně koncipované koprogramy/coroutines vytváří jen s využitím k tomu určených funkcí, což je poněkud flexibilnější řešení).
2. Demonstrační příklad Test28.py: volání funkcí v Pythonu
Pro osvětlení některých sémantických rozdílů mezi programovacími jazyky Lua a Python byl vytvořen dnešní první demonstrační příklad nazvaný Test28.py. Jsou v něm implementovány funkce se jmény function1(), function2() a function3(), přičemž funkce function1() neočekává žádné argumenty, funkce function2() očekává jeden argument a konečně funkce function3() očekává dva argumenty, které jsou sečteny (přesněji řečeno je na ně aplikován operátor +, který lze přetížit). Všechny tři zmíněné funkce jsou volány z kódu, který je implementován v další trojici funkcí nazvaných callFunction1(), callFunction2() a callFunction3(), přičemž se počet skutečně použitých parametrů mění od nuly (žádné parametry) do dvou.
#
# Demonstracni priklad cislo 28.
#
# Volani funkci v programovacim jazyce Python.
#
#
# Funkce bez parametru.
#
def function1():
pass
#
# Funkce s jednim parametrem.
#
def function2(x):
return x
#
# Funkce se dvema parametry.
#
def function3(x, y):
if x and y:
return x+y
else:
return None
#
# Volani funkce function1().
#
def callFunction1():
function1()
function1(None)
function1(42)
function1(1, 2)
function1("xyzzy")
#
# Volani funkce function2().
#
def callFunction2():
function2()
function2(None)
function2(42)
function2(1, 2)
function2("xyzzy")
#
# Volani funkce function3().
#
def callFunction3():
function3()
function3(None)
function3(42)
function3(1, 2)
function3("xyzzy")
#
# Spusteni testu.
#
def main():
callFunction1()
callFunction2()
callFunction3()
#
# Ukazka disasembleru.
# (prekladu funkci do bajtkodu Python VM).
#
def disassemble():
from dis import dis
print("\nfunction1():")
dis(function1)
print("\nfunction2():")
dis(function2)
print("\nfunction3():")
dis(function3)
print("\ncallFunction1():")
dis(callFunction1)
print("\ncallFunction2():")
dis(callFunction2)
print("\ncallFunction3():")
dis(callFunction3)
main()
#disassemble()
#
# Finito.
#
Tento demonstrační příklad po svém spuštění skončí s chybou, protože v programovacím jazyku Python se kontroluje přesný počet (nikoli typ) argumentů, a to ve chvíli volání funkce (tj. v runtime, nikoli v compile time):
Traceback (most recent call last):
File "Test28.py", line 94, in <module>
main()
File "Test28.py", line 76, in main
callFunction1()
File "Test28.py", line 41, in callFunction1
function1(None)
TypeError: function1() takes no arguments (1 given)
Podobně koncipovaný příklad naprogramovaný v Lue lze však spustit bez problémů, jelikož zde jsou přebytečné argumenty ignorovány (popř. uloženy do tabulky) a naopak chybějící argumenty jsou ve chvíli volání funkce inicializovány na nil:
--
-- Demonstracni priklad cislo 25.
--
-- Volani funkci v programovacim jazyce Lua.
--
--
-- Funkce bez parametru.
--
function function1()
end
--
-- Funkce s jednim parametrem.
--
function function2(x)
return x
end
--
-- Funkce se dvema parametry.
--
function function3(x, y)
if x and y then
return x+y
else
return nil
end
end
--
-- Volani funkce function1().
--
function callFunction1()
function1()
function1(nil)
function1(42)
function1(1, 2)
function1("xyzzy")
end
--
-- Volani funkce function2().
--
function callFunction2()
function2()
function2(nil)
function2(42)
function2(1, 2)
function2("xyzzy")
end
--
-- Volani funkce function3().
--
function callFunction3()
function3()
function3(nil)
function3(42)
function3(1, 2)
function3("xyzzy")
end
--
-- Spusteni testu.
--
function main()
callFunction1()
callFunction2()
callFunction3()
end
main()
--
-- Finito.
--
3. Překlad demonstračního příkladu Test28.py do bajtkódu Python VM
I když při běhu demonstračního příkladu Test28.py dojde k (běhové) chybě, je překlad všech funkcí do bajtkódu Python VM proveden korektně, o čemž se ostatně můžeme snadno přesvědčit:
function1():
13 0 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
3 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
function2():
21 0 LOAD_FAST 0 (x) // uložit první (a jediný) parametr funkce na TOS
3 RETURN_VALUE // vrátit tuto hodnotu
function3():
29 0 LOAD_FAST 0 (x) // uložit první parametr funkce na TOS
3 JUMP_IF_FALSE 16 (to 22) // skok pokud je první parametr None/false
6 POP_TOP // odstranit prvek z TOS
7 LOAD_FAST 1 (y) // uložit druhý parametr funkce na TOS
10 JUMP_IF_FALSE 9 (to 22) // skok pokud je druhý parametr None/false
13 POP_TOP // odstranit prvek z TOS
30 14 LOAD_FAST 0 (x) // uložit první parametr funkce na TOS
17 LOAD_FAST 1 (y) // uložit druhý parametr funkce na TOS
20 BINARY_ADD // aplikace operátoru +
21 RETURN_VALUE // vrátit tuto hodnotu
22 POP_TOP // odstranit prvek z TOS
32 23 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
26 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
27 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
30 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
(zde evidentně neproběhla žádná optimalizace bajtkódu, ostatně podobně jako v případě JVM i Lua VM).
callFunction1():
40 0 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
3 CALL_FUNCTION 0 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
6 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
41 7 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
10 LOAD_CONST 0 (None) // skutečný parametr funkce uložit na TOS
13 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
16 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
42 17 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
20 LOAD_CONST 1 (42) // skutečný parametr funkce uložit na TOS
23 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
26 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
43 27 LOAD_GLOBAL 2 (function1) // načíst referenci volané funkce
30 LOAD_CONST 2 (1) // skutečný parametr funkce uložit na TOS
33 LOAD_CONST 3 (2) // skutečný parametr funkce uložit na TOS
36 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
39 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
44 40 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
43 LOAD_CONST 4 ('xyzzy') // skutečný parametr funkce uložit na TOS
46 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
49 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
50 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
53 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
callFunction2():
52 0 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
3 CALL_FUNCTION 0 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
6 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
53 7 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
10 LOAD_CONST 0 (None) // skutečný parametr funkce uložit na TOS
13 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
16 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
54 17 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
20 LOAD_CONST 1 (42) // skutečný parametr funkce uložit na TOS
23 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
26 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
55 27 LOAD_GLOBAL 2 (function2) // načíst referenci volané funkce
30 LOAD_CONST 2 (1) // skutečný parametr funkce uložit na TOS
33 LOAD_CONST 3 (2) // skutečný parametr funkce uložit na TOS
36 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
39 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
56 40 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
43 LOAD_CONST 4 ('xyzzy') // skutečný parametr funkce uložit na TOS
46 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
49 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
50 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
53 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
callFunction3():
64 0 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
3 CALL_FUNCTION 0 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
6 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
65 7 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
10 LOAD_CONST 0 (None) // skutečný parametr funkce uložit na TOS
13 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
16 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
66 17 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
20 LOAD_CONST 1 (42) // skutečný parametr funkce uložit na TOS
23 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
26 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
67 27 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
30 LOAD_CONST 2 (1) // skutečný parametr funkce uložit na TOS
33 LOAD_CONST 3 (2) // skutečný parametr funkce uložit na TOS
36 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
39 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
68 40 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
43 LOAD_CONST 4 ('xyzzy') // skutečný parametr funkce uložit na TOS
46 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
49 POP_TOP // odstranit prvek (návratovou hodnotu) z TOS
50 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
53 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
Z tohoto výpisu bajtkódu je způsob volání funkcí jasně patrný:
- Reference na volanou funkci je uložena na zásobník operandů
- Skutečné parametry jsou před voláním funkce předány (uloženy) na zásobník operandů
- Funkce je volána pomocí instrukce CALL_FUNCTION
- Návrat z funkce je řešen přes instrukci RETURN_VALUE
- Návratová hodnota je po návratu z volané funkce opět uložena na TOS zásobníku operandů
4. Demonstrační příklad Test29.py: volání funkcí v Pythonu
Dnešní druhý demonstrační příklad, který je nazvaný Test29.py, je již naprogramován korektně, tj. program po svém spuštění poběží bez chyby. V tomto příkladu jsou opět deklarovány tři funkce pojmenované function1(), function2() a function3(), přičemž první funkce neočekává žádné parametry, funkce druhá očekává jeden parametr a funkce třetí dva parametry. Způsob volání těchto tří funkcí je otestován v další trojici funkcí nazvaných callFunction1(), callFunction2() a callFunction3(). Povšimněte si, že se předávají parametry různého typu; konkrétně u funkce function3() je možné použít jakékoli dva parametry, na něž lze aplikovat operátor + (ten je zde mnohem flexibilnější, než operátor + v jazyce Lua nebo Java):
#
# Demonstracni priklad cislo 29.
#
# Volani funkci v programovacim jazyce Python.
#
#
# Funkce bez parametru.
#
def function1():
pass
#
# Funkce s jednim parametrem.
#
def function2(x):
return x
#
# Funkce se dvema parametry.
#
def function3(x, y):
return x+y
#
# Volani funkce function1().
#
def callFunction1():
result = function1()
print(result)
#
# Volani funkce function2().
#
def callFunction2():
result1 = function2(None)
result2 = function2(42)
result3 = function2("xyzzy")
result4 = function2([1,2,3])
result5 = function2((1,2,3))
print(result1)
print(result2)
print(result3)
print(result4)
print(result5)
#
# Volani funkce function3().
#
def callFunction3():
result1 = function3(1, 2)
result2 = function3("Hello ", "world")
result3 = function3([1,2,3], [4,5,6])
result4 = function3((1,2,3), (4,5,6))
result5 = function3(["jedna", "dve"], ["Freddy", "jde"])
print(result1)
print(result2)
print(result3)
print(result4)
print(result5)
#
# Spusteni testu.
#
def main():
callFunction1()
callFunction2()
callFunction3()
#
# Ukazka disasembleru.
# (prekladu funkci do bajtkodu Python VM).
#
def disassemble():
from dis import dis
print("\nfunction1():")
dis(function1)
print("\nfunction2():")
dis(function2)
print("\nfunction3():")
dis(function3)
print("\ncallFunction1():")
dis(callFunction1)
print("\ncallFunction2():")
dis(callFunction2)
print("\ncallFunction3():")
dis(callFunction3)
main()
#disassemble()
#
# Finito.
#
Spuštění tohoto demonstračního příkladu již proběhne bez problémů, což je ostatně patrné i z následujícího výpisu:
None None 42 xyzzy [1, 2, 3] (1, 2, 3) 3 Hello world [1, 2, 3, 4, 5, 6] (1, 2, 3, 4, 5, 6) ['jedna', 'dve', 'Freddy', 'jde']
5. Překlad demonstračního příkladu Test29.py do bajtkódu Python VM
Opět se podívejme na způsob překladu tohoto demonstračního příkladu do bajtkódu Python VM:
function1():
13 0 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
3 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
function2():
21 0 LOAD_FAST 0 (x) // uložit první (a jediný) parametr funkce na TOS
3 RETURN_VALUE // vrátit tuto hodnotu
function3():
29 0 LOAD_FAST 0 (x) // uložit první parametr funkce na TOS
3 LOAD_FAST 1 (y) // uložit druhý parametr funkce na TOS
6 BINARY_ADD // aplikovat operátor + na oba operandy (parametry)
7 RETURN_VALUE // vrátit výsledek operace +
Způsob volání těchto funkcí:
callFunction1():
37 0 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
3 CALL_FUNCTION 0 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
6 STORE_FAST 0 (result) // uložit výsledek volání funkce do lokální proměnné
38 9 LOAD_FAST 0 (result) // načíst výsledek předchozího volání funkce
12 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
13 PRINT_NEWLINE // odřádkování
14 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
17 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
callFunction2():
46 0 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
3 LOAD_CONST 0 (None) // skutečný parametr funkce uložit na TOS
6 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
9 STORE_FAST 0 (result1) // uložit výsledek volání funkce do lokální proměnné
47 12 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
15 LOAD_CONST 1 (42) // skutečný parametr funkce uložit na TOS
18 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
21 STORE_FAST 1 (result2) // uložit výsledek volání funkce do lokální proměnné
48 24 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
27 LOAD_CONST 2 ('xyzzy') // skutečný parametr funkce uložit na TOS
30 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
33 STORE_FAST 2 (result3) // uložit výsledek volání funkce do lokální proměnné
49 36 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
39 LOAD_CONST 3 (1)
42 LOAD_CONST 4 (2)
45 LOAD_CONST 5 (3)
48 BUILD_LIST 3 // vytvoření seznamu ze tří prvků
51 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
54 STORE_FAST 3 (result4) // uložit výsledek volání funkce do lokální proměnné
50 57 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
60 LOAD_CONST 6 ((1, 2, 3)) // skutečný parametr funkce uložit na TOS
63 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
66 STORE_FAST 4 (result5) // uložit výsledek volání funkce do lokální proměnné
51 69 LOAD_FAST 0 (result1) // načíst výsledek předchozího volání funkce
72 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
73 PRINT_NEWLINE // odřádkování
52 74 LOAD_FAST 1 (result2) // načíst výsledek předchozího volání funkce
77 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
78 PRINT_NEWLINE // odřádkování
53 79 LOAD_FAST 2 (result3) // načíst výsledek předchozího volání funkce
82 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
83 PRINT_NEWLINE // odřádkování
54 84 LOAD_FAST 3 (result4) // načíst výsledek předchozího volání funkce
87 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
88 PRINT_NEWLINE // odřádkování
55 89 LOAD_FAST 4 (result5) // načíst výsledek předchozího volání funkce
92 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
93 PRINT_NEWLINE // odřádkování
94 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
97 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
(povšimněte si, jak elegantně jsou n-tice reprezentovány konstantami, zatímco seznamy se musí dynamicky tvořit)
callFunction3():
63 0 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
3 LOAD_CONST 1 (1) // skutečný parametr funkce uložit na TOS
6 LOAD_CONST 2 (2) // skutečný parametr funkce uložit na TOS
9 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
12 STORE_FAST 0 (result1) // uložit výsledek volání funkce do lokální proměnné
64 15 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
18 LOAD_CONST 3 ('Hello ') // skutečný parametr funkce uložit na TOS
21 LOAD_CONST 4 ('world') // skutečný parametr funkce uložit na TOS
24 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
27 STORE_FAST 1 (result2) // uložit výsledek volání funkce do lokální proměnné
65 30 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
33 LOAD_CONST 1 (1)
36 LOAD_CONST 2 (2)
39 LOAD_CONST 5 (3)
42 BUILD_LIST 3 // vytvoření seznamu ze tří prvků
45 LOAD_CONST 6 (4)
48 LOAD_CONST 7 (5)
51 LOAD_CONST 8 (6)
54 BUILD_LIST 3 // vytvoření seznamu ze tří prvků
57 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
60 STORE_FAST 2 (result3) // uložit výsledek volání funkce do lokální proměnné
66 63 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
66 LOAD_CONST 13 ((1, 2, 3)) // skutečný parametr funkce uložit na TOS
69 LOAD_CONST 14 ((4, 5, 6)) // skutečný parametr funkce uložit na TOS
72 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
75 STORE_FAST 3 (result4) // uložit výsledek volání funkce do lokální proměnné
67 78 LOAD_GLOBAL 0 (function3) // načíst referenci volané funkce
81 LOAD_CONST 9 ('jedna')
84 LOAD_CONST 10 ('dve')
87 BUILD_LIST 2 // vytvoření seznamu ze dvou prvků
90 LOAD_CONST 11 ('Freddy')
93 LOAD_CONST 12 ('jde')
96 BUILD_LIST 2 // vytvoření seznamu ze dvou prvků
99 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
102 STORE_FAST 4 (result5) // uložit výsledek volání funkce do lokální proměnné
69 105 LOAD_FAST 0 (result1) // načíst výsledek předchozího volání funkce
108 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
109 PRINT_NEWLINE // odřádkování
70 110 LOAD_FAST 1 (result2) // načíst výsledek předchozího volání funkce
113 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
114 PRINT_NEWLINE // odřádkování
71 115 LOAD_FAST 2 (result3) // načíst výsledek předchozího volání funkce
118 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
119 PRINT_NEWLINE // odřádkování
72 120 LOAD_FAST 3 (result4) // načíst výsledek předchozího volání funkce
123 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
124 PRINT_NEWLINE // odřádkování
73 125 LOAD_FAST 4 (result5) // načíst výsledek předchozího volání funkce
128 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
129 PRINT_NEWLINE // odřádkování
130 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
133 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
6. Demonstrační příklad Test30.py: funkce s pojmenovanými parametry
Dnešní třetí demonstrační příklad, jenž nese jméno Test30.py, bude využit pro zjištění, jakým způsobem se bude do bajtkódu Python VM překládat volání funkcí s využitím pojmenovaných parametrů. Volané funkce jsou v tomto příkladu dvě – funkce function1() očekávající jeden parametr a funkce function2() očekávající parametry dva. Volání těchto funkcí různými způsoby je realizováno v callFunction1() a callFunction2:
#
# Demonstracni priklad cislo 30.
#
# Volani funkci s pojmenovanymi parametry v Pythonu.
#
#
# Funkce s jednim parametrem.
#
def function1(x):
return x
#
# Funkce se dvema parametry.
#
def function2(x, y):
return x+y
#
# Volani funkce function1().
#
def callFunction1():
result1 = function1(42)
result2 = function1(x=42)
print(result1)
print(result2)
#
# Volani funkce function2().
#
def callFunction2():
result1 = function2("Hello ", "world")
result2 = function2(x = "Hello ", y = "world")
result3 = function2(y = "Hello ", x = "world")
print(result1)
print(result2)
print(result3)
#
# Spusteni testu.
#
def main():
callFunction1()
callFunction2()
#
# Ukazka disasembleru.
# (prekladu funkci do bajtkodu Python VM).
#
def disassemble():
from dis import dis
print("\nfunction1():")
dis(function1)
print("\nfunction2():")
dis(function2)
print("\ncallFunction1():")
dis(callFunction1)
print("\ncallFunction2():")
dis(callFunction2)
main()
#disassemble()
#
# Finito.
#
Po spuštění tohoto příkladu se na standardním výstupu objeví následujících pět řádků textu:
42 42 Hello world Hello world worldHello
7. Překlad demonstračního příkladu Test30.py do bajtkódu Python VM
Opět se podívejme na způsob překladu tohoto demonstračního příkladu. Zajímat nás bude především technika deklarace testovacích funkcí i způsob jejich volání:
function1():
13 0 LOAD_FAST 0 (x) // načíst první parametr
3 RETURN_VALUE // a vrátit ho
function2():
21 0 LOAD_FAST 0 (x) // načíst první parametr
3 LOAD_FAST 1 (y) // načíst druhý parametr
6 BINARY_ADD // sečíst hodnoty obou parametrů
7 RETURN_VALUE // vrátit výsledek součtu
Ve způsobu deklarací obou funkcí k žádným změnám nedošlo (což je pochopitelní).
callFunction1():
29 0 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
3 LOAD_CONST 1 (42) // parametr funkce uložit na TOS zásobníku operandů
6 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
9 STORE_FAST 0 (result1) // uložit výsledek volání funkce do lokální proměnné
30 12 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
15 LOAD_CONST 2 ('x') // uložit identifikátor parametru na TOS zásobníku operandů
18 LOAD_CONST 1 (42) // parametr funkce uložit na TOS zásobníku operandů
21 CALL_FUNCTION 256 // volat funkci, nulový počet pozičních parametrů, jeden pojmenovaný parametr
24 STORE_FAST 1 (result2) // uložit výsledek volání funkce do lokální proměnné
31 27 LOAD_FAST 0 (result1) // načíst výsledek předchozího volání funkce
30 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
31 PRINT_NEWLINE // odřádkování
32 32 LOAD_FAST 1 (result2) // načíst výsledek předchozího volání funkce
35 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
36 PRINT_NEWLINE // odřádkování
37 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
40 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
Zde již můžeme vidět jednu velkou změnu – za instrukcí CALL_FUNCTION je uložena hodnota 256. Ve skutečnosti se jedná o dvě hodnoty, každá o šířce osmi bitů, které byly spojeny do jednoho šestnáctibitového slova. Hodnota spodního bajtu udává počet pozičních parametrů (zde 0), hodnota horního bajtu počet pojmenovaných parametrů (zde 1 – každý pojmenovaný parametr se skládá ze svého jména a skutečné hodnoty).
callFunction2():
40 0 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
3 LOAD_CONST 1 ('Hello ') // první parametr funkce uložit na TOS zásobníku operandů
6 LOAD_CONST 2 ('world') // druhý parametr funkce uložit na TOS zásobníku operandů
9 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
12 STORE_FAST 0 (result1) // uložit výsledek volání funkce do lokální proměnné
41 15 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
18 LOAD_CONST 3 ('x') // uložit identifikátor parametru na TOS zásobníku operandů
21 LOAD_CONST 1 ('Hello ') // parametr funkce uložit na TOS zásobníku operandů
24 LOAD_CONST 4 ('y') // uložit identifikátor parametru na TOS zásobníku operandů
27 LOAD_CONST 2 ('world') // parametr funkce uložit na TOS zásobníku operandů
30 CALL_FUNCTION 512 // volat funkci se dvěma pojmenovanými parametry (512=256*2)
33 STORE_FAST 1 (result2) // uložit výsledek volání funkce do lokální proměnné
42 36 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
39 LOAD_CONST 4 ('y') // uložit identifikátor parametru na TOS zásobníku operandů
42 LOAD_CONST 1 ('Hello ') // parametr funkce uložit na TOS zásobníku operandů
45 LOAD_CONST 3 ('x') // uložit identifikátor parametru na TOS zásobníku operandů
48 LOAD_CONST 2 ('world') // parametr funkce uložit na TOS zásobníku operandů
51 CALL_FUNCTION 512 // volat funkci se dvěma pojmenovanými parametry (512=256*2)
54 STORE_FAST 2 (result3) // uložit výsledek volání funkce do lokální proměnné
44 57 LOAD_FAST 0 (result1) // načíst výsledek předchozího volání funkce
60 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
61 PRINT_NEWLINE // odřádkování
45 62 LOAD_FAST 1 (result2) // načíst výsledek předchozího volání funkce
65 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
66 PRINT_NEWLINE // odřádkování
46 67 LOAD_FAST 2 (result3) // načíst výsledek předchozího volání funkce
70 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
71 PRINT_NEWLINE // odřádkování
72 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
75 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
8. Demonstrační příklad Test31.py: parametry s implicitní hodnotou
Dnešní čtvrtý a současně i poslední demonstrační příklad pojmenovaný Test31.py používá v testovacích funkcích function1() a function2() parametry s implicitní hodnotou, tj. pokud se tyto parametry při volání funkce explicitně nezadají, je namísto nich použita hodnota získaná z hlavičky funkce. Podívejme se na zdrojový kód tohoto demonstračního příkladu:
#
# Demonstracni priklad cislo 31.
#
# Volani funkci s parametry s implicitni hodnotou v Pythonu.
#
#
# Funkce s jednim parametrem.
#
def function1(x = 1):
return x
#
# Funkce se dvema parametry.
#
def function2(x = 1, y = 2):
return x+y
#
# Volani funkce function1().
#
def callFunction1():
result1 = function1()
result2 = function1(42)
result3 = function1(x=42)
print(result1)
print(result2)
print(result3)
#
# Volani funkce function2().
#
def callFunction2():
result1 = function2()
result2 = function2(0)
result2 = function2(0,1)
result3 = function2("Hello ", "world")
result4 = function2(x = "Hello ", y = "world")
result5 = function2(y = "Hello ", x = "world")
print(result1)
print(result2)
print(result3)
print(result4)
print(result5)
#
# Spusteni testu.
#
def main():
callFunction1()
callFunction2()
#
# Ukazka disasembleru.
# (prekladu funkci do bajtkodu Python VM).
#
def disassemble():
from dis import dis
print("\nfunction1():")
dis(function1)
print("\nfunction2():")
dis(function2)
print("\ncallFunction1():")
dis(callFunction1)
print("\ncallFunction2():")
dis(callFunction2)
main()
#disassemble()
#
# Finito.
#
Po spuštění tohoto demonstračního příkladu by se měly na standardním výstupu objevit následující zprávy:
1 42 42 3 1 Hello world Hello world worldHello
9. Překlad demonstračního příkladu Test31.py do bajtkódu Python VM
Deklarace obou testovacích funkcí je kupodivu stále stejná, implicitní hodnota parametrů je totiž specifikována při vytváření funkcí instrukcí MAKE_FUNCTION:
function1():
13 0 LOAD_FAST 0 (x) // načíst první parametr
3 RETURN_VALUE // a vrátit ho
function2():
21 0 LOAD_FAST 0 (x) // načíst první parametr
3 LOAD_FAST 1 (y) // načíst druhý parametr
6 BINARY_ADD // sečíst hodnoty obou parametrů
7 RETURN_VALUE // vrátit výsledek součtu
Volání se však již odlišuje:
callFunction1():
29 0 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
3 CALL_FUNCTION 0 // volání funkce s nulovým počtem explicitně zadaných parametrů
6 STORE_FAST 0 (result1) // uložit výsledek volání funkce do lokální proměnné
30 9 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
12 LOAD_CONST 1 (42) // parametr funkce uložit na TOS zásobníku operandů
15 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
18 STORE_FAST 1 (result2) // uložit výsledek volání funkce do lokální proměnné
31 21 LOAD_GLOBAL 0 (function1) // načíst referenci volané funkce
24 LOAD_CONST 2 ('x') // uložit identifikátor parametru na TOS zásobníku operandů
27 LOAD_CONST 1 (42) // parametr funkce uložit na TOS zásobníku operandů
30 CALL_FUNCTION 256 // volat funkci, nulový počet pozičních parametrů, jeden pojmenovaný parametr
33 STORE_FAST 2 (result3) // uložit výsledek volání funkce do lokální proměnné
32 36 LOAD_FAST 0 (result1) // načíst výsledek předchozího volání funkce
39 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
40 PRINT_NEWLINE // odřádkování
33 41 LOAD_FAST 1 (result2) // načíst výsledek předchozího volání funkce
44 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
45 PRINT_NEWLINE // odřádkování
34 46 LOAD_FAST 2 (result3) // načíst výsledek předchozího volání funkce
49 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
50 PRINT_NEWLINE // odřádkování
51 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
54 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
Ještě zajímavější jsou možnosti volání funkce function2():
callFunction2():
42 0 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
3 CALL_FUNCTION 0 // volání funkce s nulovým počtem explicitně zadaných parametrů
6 STORE_FAST 0 (result1) // uložit výsledek volání funkce do lokální proměnné
43 9 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
12 LOAD_CONST 1 (0) // parametr funkce uložit na TOS zásobníku operandů
15 CALL_FUNCTION 1 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
// druhý parametr má implicitní hodnotu
18 STORE_FAST 1 (result2) // uložit výsledek volání funkce do lokální proměnné
44 21 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
24 LOAD_CONST 1 (0) // první parametr funkce uložit na TOS zásobníku operandů
27 LOAD_CONST 2 (1) // druhý parametr funkce uložit na TOS zásobníku operandů
30 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
33 STORE_FAST 1 (result2) // uložit výsledek volání funkce do lokální proměnné
45 36 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
39 LOAD_CONST 3 ('Hello ') // první parametr funkce uložit na TOS zásobníku operandů
42 LOAD_CONST 4 ('world') // druhý parametr funkce uložit na TOS zásobníku operandů
45 CALL_FUNCTION 2 // volat funkci (konstanta udává počet skutečně předávaných parametrů)
48 STORE_FAST 2 (result3) // uložit výsledek volání funkce do lokální proměnné
46 51 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
54 LOAD_CONST 5 ('x') // uložit identifikátor parametru na TOS zásobníku operandů
57 LOAD_CONST 3 ('Hello ') // parametr funkce uložit na TOS zásobníku operandů
60 LOAD_CONST 6 ('y') // uložit identifikátor parametru na TOS zásobníku operandů
63 LOAD_CONST 4 ('world') // parametr funkce uložit na TOS zásobníku operandů
66 CALL_FUNCTION 512 // volat funkci se dvěma pojmenovanými parametry (512=256*2)
69 STORE_FAST 3 (result4) // uložit výsledek volání funkce do lokální proměnné
47 72 LOAD_GLOBAL 0 (function2) // načíst referenci volané funkce
75 LOAD_CONST 6 ('y') // uložit identifikátor parametru na TOS zásobníku operandů
78 LOAD_CONST 3 ('Hello ') // parametr funkce uložit na TOS zásobníku operandů
81 LOAD_CONST 5 ('x') // uložit identifikátor parametru na TOS zásobníku operandů
84 LOAD_CONST 4 ('world') // parametr funkce uložit na TOS zásobníku operandů
87 CALL_FUNCTION 512 // volat funkci se dvěma pojmenovanými parametry (512=256*2)
90 STORE_FAST 4 (result5) // uložit výsledek volání funkce do lokální proměnné
49 93 LOAD_FAST 0 (result1) // načíst výsledek předchozího volání funkce
96 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
97 PRINT_NEWLINE // odřádkování
50 98 LOAD_FAST 1 (result2) // načíst výsledek předchozího volání funkce
101 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
102 PRINT_NEWLINE // odřádkování
51 103 LOAD_FAST 2 (result3) // načíst výsledek předchozího volání funkce
106 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
107 PRINT_NEWLINE // odřádkování
52 108 LOAD_FAST 3 (result4) // načíst výsledek předchozího volání funkce
111 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
112 PRINT_NEWLINE // odřádkování
53 113 LOAD_FAST 4 (result5) // načíst výsledek předchozího volání funkce
116 PRINT_ITEM // tisk výsledku (návratové hodnoty volané funkce)
117 PRINT_NEWLINE // odřádkování
118 LOAD_CONST 0 (None) // uložit konstantu None na zásobník operandů
121 RETURN_VALUE // vrátit tuto konstantu (uloženou na TOS)
10. Uzávěry a generátory v Pythonu
V předchozích čtyřech demonstračních příkladech jsme si ukázali pouze základní způsoby volání funkcí. Možnosti Pythonu jsou v tomto směru totiž mnohem větší než v Javě a dokonce větší než v jazyku Lua, protože kromě parametrů s implicitní hodnotou a pojmenovaných parametrů lze při deklaraci a/nebo volání funkcí použít i další možnosti, například proměnný počet parametrů, tzv. keyword parametry či jejich kombinaci. Navíc i Python podporuje uzávěry (closures), i když ne v takové míře, jako je tomu v Lue, Perlu či JavaScriptu. Aby toho nebylo málo, podporuje programovací jazyk Python i bajtkód jeho virtuálního stroje takzvané generátory. Všechny instrukce bajtkódu Python VM související s voláním funkcí jsou vypsány v další tabulce, přičemž podrobnosti k MAKE_CLOSURE, YIELD_VALUE atd. si řekneme v následující části tohoto seriálu:
| # | Instrukce | Stručný popis |
|---|---|---|
| 1 | MAKE_FUNCTION | vytvoření funkce |
| 2 | MAKE_CLOSURE | vytvoření uzávěru |
| 3 | CALL_FUNCTION | volání funkce s pozičními a/nebo pojmenovanými parametry |
| 4 | CALL_FUNCTION_VAR | volání funkce s proměnným počtem parametrů |
| 5 | CALL_FUNCTION_KW | volání funkce s keyword parametry |
| 6 | CALL_FUNCTION_VAR_KW | kombinace předchozích dvou možností |
| 7 | YIELD_VALUE | vrácení (další) hodnoty z generátoru |
11. Repositář se zdrojovými kódy všech čtyř dnešních demonstračních příkladů
Všechny čtyři dnes popsané a použité demonstrační příklady byly uloženy do Mercurial repositáře umístěného na adrese http://icedtea.classpath.org/people/ptisnovs/jvm-tools/. Odkazy na prozatím poslední verze těchto čtyř příkladů naleznete v tabulce pod tímto odstavcem:
12. Odkazy na Internetu
- Python Byte Code Instructions
https://docs.python.org/release/2.5.2/lib/bytecodes.html - Python 2.x: funkce range()
https://docs.python.org/2/library/functions.html#range - Python 2.x: typ iterátor
https://docs.python.org/2/library/stdtypes.html#iterator-types - Python break, continue and pass Statements
http://www.tutorialspoint.com/python/python_loop_control.htm - Python Bytecode: Fun With Dis
http://akaptur.github.io/blog/2013/08/14/python-bytecode-fun-with-dis/ - Python's Innards: Hello, ceval.c!
http://tech.blog.aknin.name/category/my-projects/pythons-innards/ - Byterun
https://github.com/nedbat/byterun - Python Byte Code Instructions
http://document.ihg.uni-duisburg.de/Documentation/Python/lib/node56.html - Python Byte Code Instructions
https://docs.python.org/3.2/library/dis.html#python-bytecode-instructions - dis – Python module
https://docs.python.org/2/library/dis.html - Comparison of Python virtual machines
http://polishlinux.org/apps/cli/comparison-of-python-virtual-machines/ - Programming in Lua (first edition)
http://www.lua.org/pil/contents.html - Programming in Lua: 6 – More about Functions
http://www.lua.org/pil/6.html - Programming in Lua: 6.1 – Closures
http://www.lua.org/pil/6.1.html - Programming in Lua: 9.1 – Coroutine Basics
http://www.lua.org/pil/9.1.html - Programming in Lua: Numeric for
http://www.lua.org/pil/4.3.4.html - Programming in Lua: break and return
http://www.lua.org/pil/4.4.html - Programming in Lua: Tables
http://www.lua.org/pil/2.5.html - Programming in Lua: Table Constructors
http://www.lua.org/pil/3.6.html - Programovací jazyk Lua
http://palmknihy.cz/web/kniha/programovaci-jazyk-lua-12651.htm - Lua: Tables Tutorial
http://lua-users.org/wiki/TablesTutorial - Lua: Control Structure Tutorial
http://lua-users.org/wiki/ControlStructureTutorial - Lua Types Tutorial
http://lua-users.org/wiki/LuaTypesTutorial - Goto Statement in Lua
http://lua-users.org/wiki/GotoStatement - Lua 5.2 sources
http://www.lua.org/source/5.2/ - Lua 5.2 sources – lopcodes.h
http://www.lua.org/source/5.2/lopcodes.h.html - Lua 5.2 sources – lopcodes.c
http://www.lua.org/source/5.2/lopcodes.c.html - For-each Loop in Java
http://www.leepoint.net/notes-java/flow/loops/foreach.html - For Loop (Wikipedia)
http://en.wikipedia.org/wiki/For_loop - Heinz Rutishauser
http://en.wikipedia.org/wiki/Heinz_Rutishauser - Parrot
http://www.parrot.org/ - Parrot languages
http://www.parrot.org/languages - Parrot Primer
http://docs.parrot.org/parrot/latest/html/docs/intro.pod.html - Parrot Opcodes
http://docs.parrot.org/parrot/latest/html/ops.html - Parrot VM
http://en.wikibooks.org/wiki/Parrot_Virtual_Machine - Parrot Assembly Language
http://www.perl6.org/archive/pdd/pdd06_pasm.html - Parrot Reference: Chapter 11 – Perl 6 and Parrot Essentials
http://oreilly.com/perl/excerpts/perl-6-and-parrot-essentials/parrot-reference.html - O-code
http://en.wikipedia.org/wiki/O-code_machine - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - GC safe-point (or safepoint) and safe-region
http://xiao-feng.blogspot.cz/2008/01/gc-safe-point-and-safe-region.html - Safepoints in HotSpot JVM
http://blog.ragozin.info/2012/10/safepoints-in-hotspot-jvm.html - Java theory and practice: Synchronization optimizations in Mustang
http://www.ibm.com/developerworks/java/library/j-jtp10185/ - How to build hsdis
http://hg.openjdk.java.net/jdk7/hotspot/hotspot/file/tip/src/share/tools/hsdis/README - Java SE 6 Performance White Paper
http://www.oracle.com/technetwork/java/6-performance-137236.html - Lukas Stadler's Blog
http://classparser.blogspot.cz/2010/03/hsdis-i386dll.html - How to build hsdis-amd64.dll and hsdis-i386.dll on Windows
http://dropzone.nfshost.com/hsdis.htm - PrintAssembly
https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly - The Java Virtual Machine Specification: 3.14. Synchronization
http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-3.html#jvms-3.14 - The Java Virtual Machine Specification: 8.3.1.4. volatile Fields
http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.4 - The Java Virtual Machine Specification: 17.4. Memory Model
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4 - The Java Virtual Machine Specification: 17.7. Non-atomic Treatment of double and long
http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.7 - Open Source ByteCode Libraries in Java
http://java-source.net/open-source/bytecode-libraries - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - BCEL Home page
http://commons.apache.org/bcel/ - Byte Code Engineering Library (před verzí 5.0)
http://bcel.sourceforge.net/ - Byte Code Engineering Library (verze >= 5.0)
http://commons.apache.org/proper/commons-bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - Javassist
http://www.jboss.org/javassist/ - Byteman
http://www.jboss.org/byteman - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - jclasslib bytecode viewer
http://www.ej-technologies.com/products/jclasslib/overview.html