Obsah
1. Pohled pod kapotu JVM – přístup k prvkům složených datových typů v JVM, Lua VM a Python VM
2. Přístup k prvkům polí v JVM
3. Demonstrační příklad Test21.java: přístup k prvkům jednorozměrného pole
4. Demonstrační příklad Test22.java: přístup k prvkům dvourozměrného pole
5. Přístup k prvkům tabulek v Lua VM
6. Demonstrační příklad Test21.lua: přístup k prvkům jednorozměrného pole (tabulky)
7. Demonstrační příklad Test22.lua: přístup k prvkům dvourozměrného pole (tabulky)
8. Přístup k prvkům seznamů a n-tic v Python VM
9. Repositář se zdrojovými kódy všech dnešních demonstračních příkladů
1. Pohled pod kapotu JVM – přístup k prvkům složených datových typů v JVM, Lua VM a Python VM
V předchozí části tohoto seriálu jsme si kromě dalších informací řekli a současně i na několika demonstračních příkladech ukázali, jakým způsobem lze v JVM, Lua VM i Python VM vytvářet složené datové typy, tj. pole, tabulky, seznamy či n-tice. Dnes budeme v tomto tématu pokračovat, protože si ukážeme způsob přístupu k prvkům těchto typů. Řekneme si, jak lze prvky přečíst (a to například pro jednorozměrnou i vícerozměrnou variantu složeného datového typu), i jak je lze zapsat, samozřejmě ovšem jen v těch případech, kdy je to dovoleno (u n-tic v Pythonu není zápis z pochopitelných důvodů povolen). Manipulace s prvky složených datových typů je již poněkud komplikovanější, než tomu bylo v případě jednoduchých datových typů (čísel, pravdivostních hodnot, znaků), a to z toho důvodu, že příslušné instrukce bajtkódu musí zpracovat jak referenci na příslušné pole/tabulku/seznam, tak i index čteného či zapisovaného prvku. Způsob implementace těchto operací bude vysvětlen na několika příkladech.
2. Přístup k prvkům polí v JVM
Mezi další operace týkající se polí podporovaných programovacím jazykem Java patří čtení obsahu jednotlivých prvků, popř. změna hodnoty jednotlivých prvků pole. Pro tyto účely je v instrukční sadě JVM vyhrazeno šestnáct instrukcí rozdělených do dvou sad. V první sadě je osm instrukcí určených pro přečtení prvku pole a uložení přečtené hodnoty na vrchol zásobníku operandů. Před zavoláním těchto instrukcí musí být na zásobníku operandů uložena reference na pole (tj. výsledek instrukce newarray, anewarray) a taktéž index prvku, který se má přečíst. Povšimněte si, že pole s prvky typu boolean je ve skutečnosti obsluhováno takovým způsobem, jako by obsahovalo prvky typu byte:
# | Instrukce | Opkód | Operandy | Prováděná operace |
---|---|---|---|---|
1 | iaload | 0×2E | × | přečtení prvku z pole typu int[] |
2 | laload | 0×2F | × | přečtení prvku z pole typu long[] |
3 | faload | 0×30 | × | přečtení prvku z pole typu float[] |
4 | daload | 0×31 | × | přečtení prvku z pole typu double[] |
5 | aaload | 0×32 | × | přečtení prvku z pole typu reference[] |
6 | baload | 0×33 | × | přečtení prvku z pole typu byte[] nebo boolean[] |
7 | caload | 0×34 | × | přečtení prvku z pole typu char[] |
8 | saload | 0×35 | × | přečtení prvku z pole typu short[] |
Ve druhé skupině se nachází taktéž osm instrukcí. Tyto instrukce slouží pro změnu hodnoty vybraného prvku pole. Před spuštěním těchto instrukcí musí být na zásobníku operandů uložena jak reference na pole, tak i index měněného prvku a samozřejmě i jeho nová hodnota:
# | Instrukce | Opkód | Operandy | Prováděná operace |
---|---|---|---|---|
1 | iastore | 0×4F | × | zápis nové hodnoty prvku do pole typu int[] |
2 | lastore | 0×50 | × | zápis nové hodnoty prvku do pole typu long[] |
3 | fastore | 0×51 | × | zápis nové hodnoty prvku do pole typu float[] |
4 | dastore | 0×52 | × | zápis nové hodnoty prvku do pole typu double[] |
5 | aastore | 0×53 | × | zápis nové hodnoty prvku do pole typu reference[] |
6 | bastore | 0×54 | × | zápis nové hodnoty prvku do pole typu byte[] nebo boolean[] |
7 | castore | 0×55 | × | zápis nové hodnoty prvku do pole typu char[] |
8 | sastore | 0×56 | × | zápis nové hodnoty prvku do pole typu short[] |
3. Demonstrační příklad Test21.java: přístup k prvkům jednorozměrného pole
Podívejme se nyní na demonstrační příklad (pojmenovaný Test21.java), v němž se provádí přístup k prvkům jednorozměrných polí. V příkladu je implementována přetížená metoda readItem() pro čtení prvku s daným indexem z pole, dále pak přetížená metoda writeItem() pro zápis prvku do pole a konečně již poněkud složitější metoda modify(), v níž se hodnota všech prvků pole zvýší o jedničku. Všechny metody jsou přetížené z toho důvodu, protože je zapotřebí ukázat, jak se pracuje s poli různých datových typů, a to jak primitivních datových typů (byte, short, int, long, float a double), tak i s poli referenčních typů (tj. objektů, přesněji řečeno pole referencí na objekty):
/** * Pristup k prvkum jednorozmerneho pole. */ public class Test21 { /* ---------------------------------------------------------------- */ /* Pole typu byte[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z pole. */ static byte readItem(byte[] array, int index) { return array[index]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(byte[] array, int index, byte value) { array[index] = value; } /** * Zvyseni hodnoty vsech prvku pole o jednicku. */ static void modify(byte[] array) { for (int i=0; i < array.length; i++) { array[i]++; } } /* ---------------------------------------------------------------- */ /* Pole typu short[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z pole. */ static short readItem(short[] array, short index) { return array[index]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(short[] array, short index, short value) { array[index] = value; } /** * Zvyseni hodnoty vsech prvku pole o jednicku. */ static void modify(short[] array) { for (int i=0; i < array.length; i++) { array[i]++; } } /* ---------------------------------------------------------------- */ /* Pole typu int[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z pole. */ static int readItem(int[] array, int index) { return array[index]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(int[] array, int index, int value) { array[index] = value; } /** * Zvyseni hodnoty vsech prvku pole o jednicku. */ static void modify(int[] array) { for (int i=0; i < array.length; i++) { array[i]++; } } /* ---------------------------------------------------------------- */ /* Pole typu long[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z pole. */ static long readItem(long[] array, int index) { return array[index]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(long[] array, int index, long value) { array[index] = value; } /** * Zvyseni hodnoty vsech prvku pole o jednicku. */ static void modify(long[] array) { for (int i=0; i < array.length; i++) { array[i]++; } } /* ---------------------------------------------------------------- */ /* Pole typu float[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z pole. */ static float readItem(float[] array, int index) { return array[index]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(float[] array, int index, float value) { array[index] = value; } /** * Zvyseni hodnoty vsech prvku pole o jednicku. */ static void modify(float[] array) { for (int i=0; i < array.length; i++) { array[i]++; } } /* ---------------------------------------------------------------- */ /* Pole typu double[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z pole. */ static double readItem(double[] array, int index) { return array[index]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(double[] array, int index, double value) { array[index] = value; } /** * Zvyseni hodnoty vsech prvku pole o jednicku. */ static void modify(double[] array) { for (int i=0; i < array.length; i++) { array[i]++; } } /* ---------------------------------------------------------------- */ /* Pole typu Object[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z pole. */ static Object readItem(Object[] array, int index) { return array[index]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(Object[] array, int index, Object value) { array[index] = value; } }
Nejprve si ukažme, jak je přeložena přetížená metoda readItem(). Vidíme zde použití instrukcí typu *aload pro přečtení prvku z pole:
Precteni prvku z pole typu byte[] static byte readItem(byte[], int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index cteneho prvku na zasobnik operandu 2: baload // nacist prislusny prvek a ulozit ho na zasobik operandu 3: ireturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu short[] static short readItem(short[], short); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index cteneho prvku na zasobnik operandu 2: saload // nacist prislusny prvek a ulozit ho na zasobik operandu 3: ireturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu int[] static int readItem(int[], int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index cteneho prvku na zasobnik operandu 2: iaload // nacist prislusny prvek a ulozit ho na zasobik operandu 3: ireturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu long[] static long readItem(long[], int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index cteneho prvku na zasobnik operandu 2: laload // nacist prislusny prvek a ulozit ho na zasobik operandu 3: lreturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu float[] static float readItem(float[], int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index cteneho prvku na zasobnik operandu 2: faload // nacist prislusny prvek a ulozit ho na zasobik operandu 3: freturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu double[] static double readItem(double[], int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index cteneho prvku na zasobnik operandu 2: daload // nacist prislusny prvek a ulozit ho na zasobik operandu 3: dreturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu Object[] static java.lang.Object readItem(java.lang.Object[], int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index cteneho prvku na zasobnik operandu 2: aaload // nacist prislusny prvek a ulozit ho na zasobik operandu 3: areturn // vratit prislusny prvek precteny z pole
V přetížené metodě writeItem() se po překladu používají instrukce *astore:
Zapis prvku do pole typu byte[] static void writeItem(byte[], int, byte); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index zapisovaneho prvku na zasobnik operandu 2: iload_2 // ulozit novou (zapisovanou) hodnotu prvku na zasobnik operandu 3: bastore // zapsat prvek do pole a soucasne odstranit tri polozky ze zasobniku 4: return // navrat z metody Zapis prvku do pole typu short[] static void writeItem(short[], short, short); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index zapisovaneho prvku na zasobnik operandu 2: iload_2 // ulozit novou (zapisovanou) hodnotu prvku na zasobnik operandu 3: sastore // zapsat prvek do pole a soucasne odstranit tri polozky ze zasobniku 4: return // vratit prislusny prvek precteny z pole Zapis prvku do pole typu int[] static void writeItem(int[], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index zapisovaneho prvku na zasobnik operandu 2: iload_2 // ulozit novou (zapisovanou) hodnotu prvku na zasobnik operandu 3: iastore // zapsat prvek do pole a soucasne odstranit tri polozky ze zasobniku 4: return // vratit prislusny prvek precteny z pole Zapis prvku do pole typu long[] static void writeItem(long[], int, long); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index zapisovaneho prvku na zasobnik operandu 2: lload_2 // ulozit novou (zapisovanou) hodnotu prvku na zasobnik operandu 3: lastore // zapsat prvek do pole a soucasne odstranit tri polozky ze zasobniku 4: return // vratit prislusny prvek precteny z pole Zapis prvku do pole typu float[] static void writeItem(float[], int, float); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index zapisovaneho prvku na zasobnik operandu 2: fload_2 // ulozit novou (zapisovanou) hodnotu prvku na zasobnik operandu 3: fastore // zapsat prvek do pole a soucasne odstranit tri polozky ze zasobniku 4: return // vratit prislusny prvek precteny z pole Zapis prvku do pole typu double[] static void writeItem(double[], int, double); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index zapisovaneho prvku na zasobnik operandu 2: dload_2 // ulozit novou (zapisovanou) hodnotu prvku na zasobnik operandu 3: dastore // zapsat prvek do pole a soucasne odstranit tri polozky ze zasobniku 4: return // vratit prislusny prvek precteny z pole Zapis prvku do pole typu Object[] static void writeItem(java.lang.Object[], int, java.lang.Object); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit index zapisovaneho prvku na zasobnik operandu 2: aload_2 // ulozit novou (zapisovanou) hodnotu prvku na zasobnik operandu 3: aastore // zapsat prvek do pole a soucasne odstranit tri polozky ze zasobniku 4: return // vratit prislusny prvek precteny z pole
V metodě modify() můžeme vidět kombinaci čtení i zápisu hodnot prvků do polí:
Operace s prvky typu byte[] static void modify(byte[]); Code: 0: iconst_0 1: istore_1 // lokalni promenna pouzita ve funkci pocitadla smycky 2: iload_1 // hodnota pocitadla na zasobnik 3: aload_0 // ulozit referenci na pole na zasobnik operandu 4: arraylength // vypocitat delku pole (pocet prvku) 5: if_icmpge 22 // podminka na zacatku iterace 8: aload_0 // ulozit referenci na pole na zasobnik operandu 9: iload_1 // hodnota pocitadla na zasobnik 10: dup2 // pocitadlo bude pouzito 2x - i pro zapis 11: baload // nacist aktualni hodnotu prvku a ulozit ji na zasobnik operandu 12: iconst_1 13: iadd // pricist jednicku k puvodni hodnote prvku 14: i2b // konverze vysledku 15: bastore // ulozit novou hodnotu prvku do pole 16: iinc 1, 1 // zvysit hodnotu pocitadla o jednicku 19: goto 2 // skok na zacatek programove smycky 22: return // vratit prislusny prvek precteny z pole Operace s prvky typu short[] static void modify(short[]); Code: 0: iconst_0 1: istore_1 // lokalni promenna pouzita ve funkci pocitadla smycky 2: iload_1 // hodnota pocitadla na zasobnik 3: aload_0 // ulozit referenci na pole na zasobnik operandu 4: arraylength // vypocitat delku pole (pocet prvku) 5: if_icmpge 22 // podminka na zacatku iterace 8: aload_0 // ulozit referenci na pole na zasobnik operandu 9: iload_1 // hodnota pocitadla na zasobnik 10: dup2 // pocitadlo bude pouzito 2x - i pro zapis 11: saload // nacist aktualni hodnotu prvku a ulozit ji na zasobnik operandu 12: iconst_1 13: iadd // pricist jednicku k puvodni hodnote prvku 14: i2s // konverze vysledku 15: sastore // ulozit novou hodnotu prvku do pole 16: iinc 1, 1 // zvysit hodnotu pocitadla o jednicku 19: goto 2 // skok na zacatek programove smycky 22: return // vratit prislusny prvek precteny z pole Operace s prvky typu int[] static void modify(int[]); Code: 0: iconst_0 1: istore_1 // lokalni promenna pouzita ve funkci pocitadla smycky 2: iload_1 // hodnota pocitadla na zasobnik 3: aload_0 // ulozit referenci na pole na zasobnik operandu 4: arraylength // vypocitat delku pole (pocet prvku) 5: if_icmpge 21 // podminka na zacatku iterace 8: aload_0 // ulozit referenci na pole na zasobnik operandu 9: iload_1 // hodnota pocitadla na zasobnik 10: dup2 // pocitadlo bude pouzito 2x - i pro zapis 11: iaload // nacist aktualni hodnotu prvku a ulozit ji na zasobnik operandu 12: iconst_1 13: iadd // pricist jednicku k puvodni hodnote prvku 14: iastore // ulozit novou hodnotu prvku do pole 15: iinc 1, 1 // zvysit hodnotu pocitadla o jednicku 18: goto 2 // skok na zacatek programove smycky 21: return // vratit prislusny prvek precteny z pole Operace s prvky typu long[] static void modify(long[]); Code: 0: iconst_0 1: istore_1 // lokalni promenna pouzita ve funkci pocitadla smycky 2: iload_1 // hodnota pocitadla na zasobnik 3: aload_0 // ulozit referenci na pole na zasobnik operandu 4: arraylength // vypocitat delku pole (pocet prvku) 5: if_icmpge 21 // podminka na zacatku iterace 8: aload_0 // ulozit referenci na pole na zasobnik operandu 9: iload_1 // hodnota pocitadla na zasobnik 10: dup2 // pocitadlo bude pouzito 2x - i pro zapis 11: laload // nacist aktualni hodnotu prvku a ulozit ji na zasobnik operandu 12: lconst_1 13: ladd // pricist jednicku k puvodni hodnote prvku 14: lastore // ulozit novou hodnotu prvku do pole 15: iinc 1, 1 // zvysit hodnotu pocitadla o jednicku 18: goto 2 // skok na zacatek programove smycky 21: return // vratit prislusny prvek precteny z pole Operace s prvky typu float[] static void modify(float[]); Code: 0: iconst_0 1: istore_1 // lokalni promenna pouzita ve funkci pocitadla smycky 2: iload_1 // hodnota pocitadla na zasobnik 3: aload_0 // ulozit referenci na pole na zasobnik operandu 4: arraylength // vypocitat delku pole (pocet prvku) 5: if_icmpge 21 // podminka na zacatku iterace 8: aload_0 // ulozit referenci na pole na zasobnik operandu 9: iload_1 // hodnota pocitadla na zasobnik 10: dup2 // pocitadlo bude pouzito 2x - i pro zapis 11: faload // nacist aktualni hodnotu prvku a ulozit ji na zasobnik operandu 12: fconst_1 13: fadd // pricist jednicku k puvodni hodnote prvku 14: fastore // ulozit novou hodnotu prvku do pole 15: iinc 1, 1 // zvysit hodnotu pocitadla o jednicku 18: goto 2 // skok na zacatek programove smycky 21: return // vratit prislusny prvek precteny z pole Operace s prvky typu double[] static void modify(double[]); Code: 0: iconst_0 1: istore_1 // lokalni promenna pouzita ve funkci pocitadla smycky 2: iload_1 // hodnota pocitadla na zasobnik 3: aload_0 // ulozit referenci na pole na zasobnik operandu 4: arraylength // vypocitat delku pole (pocet prvku) 5: if_icmpge 21 // podminka na zacatku iterace 8: aload_0 // ulozit referenci na pole na zasobnik operandu 9: iload_1 // hodnota pocitadla na zasobnik 10: dup2 // pocitadlo bude pouzito 2x - i pro zapis 11: daload // nacist aktualni hodnotu prvku a ulozit ji na zasobnik operandu 12: dconst_1 13: dadd // pricist jednicku k puvodni hodnote prvku 14: dastore // ulozit novou hodnotu prvku do pole 15: iinc 1, 1 // zvysit hodnotu pocitadla o jednicku 18: goto 2 // skok na zacatek programove smycky 21: return // vratit prislusny prvek precteny z pole
4. Demonstrační příklad Test22.java: přístup k prvkům dvourozměrného pole
Zatímco přístup k jednorozměrným polím byl vcelku přímočarý, u dvourozměrných (a samozřejmě taktéž i vícerozměrných) polí již nastává problém s přístupem k jednotlivým prvkům, a to z toho důvodu, že vícerozměrné pole je v Javě chápáno jako pole referencí na další pole o jednu dimenzi menšího. Nejprve se nicméně podívejme na zdrojový kód dalšího demonstračního příkladu, v němž se opět provádí čtení i zápis:
/** * Pristup k prvkum dvourozmerneho pole. */ public class Test22 { /* ---------------------------------------------------------------- */ /* Pole typu byte[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z dvourozmerneho pole. */ static byte readItem(byte[][] array, int index1, int index2) { return array[index1][index2]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(byte[][] array, int index1, int index2, byte value) { array[index1][index2] = value; } /** * Vypocet sumy vsech prvku */ static byte sum(byte[][] array) { byte sum = 0; for (int j = 0; j < array.length; j++) { for (int i = 0; i < array[j].length; i++) { sum += array[j][i]; } } return sum; } /* ---------------------------------------------------------------- */ /* Pole typu short[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z dvourozmerneho pole. */ static short readItem(short[][] array, int index1, int index2) { return array[index1][index2]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(short[][] array, int index1, int index2, short value) { array[index1][index2] = value; } /** * Vypocet sumy vsech prvku */ static short sum(short[][] array) { short sum = 0; for (int j = 0; j < array.length; j++) { for (int i = 0; i < array[j].length; i++) { sum += array[j][i]; } } return sum; } /* ---------------------------------------------------------------- */ /* Pole typu int[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z dvourozmerneho pole. */ static int readItem(int[][] array, int index1, int index2) { return array[index1][index2]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(int[][] array, int index1, int index2, int value) { array[index1][index2] = value; } /** * Vypocet sumy vsech prvku */ static int sum(int[][] array) { int sum = 0; for (int j = 0; j < array.length; j++) { for (int i = 0; i < array[j].length; i++) { sum += array[j][i]; } } return sum; } /* ---------------------------------------------------------------- */ /* Pole typu long[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z dvourozmerneho pole. */ static long readItem(long[][] array, int index1, int index2) { return array[index1][index2]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(long[][] array, int index1, int index2, long value) { array[index1][index2] = value; } /** * Vypocet sumy vsech prvku */ static long sum(long[][] array) { long sum = 0; for (int j = 0; j < array.length; j++) { for (int i = 0; i < array[j].length; i++) { sum += array[j][i]; } } return sum; } /* ---------------------------------------------------------------- */ /* Pole typu float[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z dvourozmerneho pole. */ static float readItem(float[][] array, int index1, int index2) { return array[index1][index2]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(float[][] array, int index1, int index2, float value) { array[index1][index2] = value; } /** * Vypocet sumy vsech prvku */ static float sum(float[][] array) { float sum = 0; for (int j = 0; j < array.length; j++) { for (int i = 0; i < array[j].length; i++) { sum += array[j][i]; } } return sum; } /* ---------------------------------------------------------------- */ /* Pole typu double[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z dvourozmerneho pole. */ static double readItem(double[][] array, int index1, int index2) { return array[index1][index2]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(double[][] array, int index1, int index2, double value) { array[index1][index2] = value; } /** * Vypocet sumy vsech prvku */ static double sum(double[][] array) { double sum = 0; for (int j = 0; j < array.length; j++) { for (int i = 0; i < array[j].length; i++) { sum += array[j][i]; } } return sum; } /* ---------------------------------------------------------------- */ /* Pole typu Object[] */ /* ---------------------------------------------------------------- */ /** * Nejjednodussi operace - precteni hodnoty prvku z dvourozmerneho pole. */ static Object readItem(Object[][] array, int index1, int index2) { return array[index1][index2]; } /** * Dalsi jednoducha operace - zapis hodnoty prvku do pole. */ static void writeItem(Object[][] array, int index1, int index2, Object value) { array[index1][index2] = value; } /** * Vypocet sumy hash kodu vsech prvku pole. */ static int sum(Object[][] array) { int sum = 0; for (int j = 0; j < array.length; j++) { for (int i = 0; i < array[j].length; i++) { sum += array[j][i].hashCode(); } } return sum; } }
Při čtení prvků z dvourozměrného pole můžeme v bajtkódu vidět použití dvojice instrukcí aaload+*aload, kde první instrukce slouží k přečtení „pole polí“ a teprve druhá instrukce přečte vlastní prvek:
Precteni prvku z pole typu byte[][] static byte readItem(byte[][], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: baload // nacist prislusny prvek a ulozit ho na zasobik operandu 5: ireturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu short[][] static short readItem(short[][], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: saload // nacist prislusny prvek a ulozit ho na zasobik operandu 5: ireturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu int[][] static int readItem(int[][], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: iaload // nacist prislusny prvek a ulozit ho na zasobik operandu 5: ireturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu long[][] static long readItem(long[][], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: laload // nacist prislusny prvek a ulozit ho na zasobik operandu 5: lreturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu float[][] static float readItem(float[][], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: faload // nacist prislusny prvek a ulozit ho na zasobik operandu 5: freturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu double[][] static double readItem(double[][], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: daload // nacist prislusny prvek a ulozit ho na zasobik operandu 5: dreturn // vratit prislusny prvek precteny z pole Precteni prvku z pole typu Object[][] static java.lang.Object readItem(java.lang.Object[][], int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: aaload // nacist prislusny prvek a ulozit ho na zasobik operandu 5: areturn // vratit prislusny prvek precteny z pole
Při zápisu prvku do pole se používá dvojice instrukcí aaload+*astore:
Zapis prvku do pole typu byte[][] static void writeItem(byte[][], int, int, byte); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: iload_3 // ulozit zapisovanou hodnotu na zasobnik 5: bastore // zmena prvku v poli 6: return // navrat z metody Zapis prvku do pole typu short[][] static void writeItem(short[][], int, int, short); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: iload_3 // ulozit zapisovanou hodnotu na zasobnik 5: sastore // zmena prvku v poli 6: return // navrat z metody Zapis prvku do pole typu int[][] static void writeItem(int[][], int, int, int); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: iload_3 // ulozit zapisovanou hodnotu na zasobnik 5: iastore // zmena prvku v poli 6: return // navrat z metody Zapis prvku do pole typu long[][] static void writeItem(long[][], int, int, long); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: lload_3 // ulozit zapisovanou hodnotu na zasobnik 5: lastore // zmena prvku v poli 6: return // navrat z metody Zapis prvku do pole typu float[][] static void writeItem(float[][], int, int, float); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: fload_3 // ulozit zapisovanou hodnotu na zasobnik 5: fastore // zmena prvku v poli 6: return // navrat z metody Zapis prvku do pole typu double[][] static void writeItem(double[][], int, int, double); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: dload_3 // ulozit zapisovanou hodnotu na zasobnik 5: dastore // zmena prvku v poli 6: return // navrat z metody Zapis prvku do pole typu Object[][] static void writeItem(java.lang.Object[][], int, int, java.lang.Object); Code: 0: aload_0 // ulozit referenci na pole na zasobnik operandu 1: iload_1 // ulozit prvni index cteneho prvku na zasobnik operandu 2: aaload // nacist n-te pole poli 3: iload_2 // ulozit druhy index cteneho prvku na zasobnik operandu 4: aload_3 // ulozit zapisovanou hodnotu na zasobnik 5: aastore // zmena prvku v poli 6: return // navrat z metody
Podívejme se i na složitější algoritmus, v němž se prochází všemi prvky dvourozměrného pole:
Operace s prvky pole typu byte[][] static byte sum(byte[][]); Code: 0: iconst_0 // konstanta 0 na vrchol zasobniku operandu 1: istore_1 // lokalni promenna pouzita ve funkci sumy 2: iconst_0 // konstanta 0 na vrchol zasobniku operandu 3: istore_2 // lokalni promenna pouzita ve funkci pocitadla vnejsi smycky 4: iload_2 // hodnota pocitadla na zasobnik 5: aload_0 // ulozit referenci na pole na zasobnik operandu 6: arraylength // vypocitat delku pole (pocet prvku) 7: if_icmpge 41 // podminka na zacatku iterace (vnejsi smycka) 10: iconst_0 // konstanta 0 na vrchol zasobniku operandu 11: istore_3 // lokalni promenna pouzita ve funkci pocitadla vnitrni smycky 12: iload_3 // hodnota pocitadla na zasobnik 13: aload_0 // ulozit referenci na pole na zasobnik operandu 14: iload_2 // pocitadlo vnejsi smycky ve funkci indexu 15: aaload // nacist i-ty prvek "vnejsiho" pole 16: arraylength // spocitat delku sub-pole 17: if_icmpge 35 // podminka na zacatku iterace (vnitrni smycka) 20: iload_1 // hodnota sumy na zasobnik 21: aload_0 // ulozit referenci na pole na zasobnik operandu 22: iload_2 23: aaload // nacist sub-pole 24: iload_3 25: baload // nacist jiz konkretni prvek pole 26: iadd // pricist k sume 27: i2b // konverze mezivysledku 28: istore_1 // ulozit mezivysledek 29: iinc 3, 1 // zvysit hodnotu pocitadla vnitrni smycky o jednicku 32: goto 12 // skok na zacatek vnitrni programove smycky 35: iinc 2, 1 // zvysit hodnotu pocitadla vnejsi smycky o jednicku 38: goto 4 // skok na zacatek vnejsi programove smycky 41: iload_1 // suma 42: ireturn // vratit hodnotu sumy Operace s prvky pole typu short[][] static short sum(short[][]); Code: 0: iconst_0 // konstanta 0 na vrchol zasobniku operandu 1: istore_1 // lokalni promenna pouzita ve funkci sumy 2: iconst_0 // konstanta 0 na vrchol zasobniku operandu 3: istore_2 // lokalni promenna pouzita ve funkci pocitadla vnejsi smycky 4: iload_2 // hodnota pocitadla na zasobnik 5: aload_0 // ulozit referenci na pole na zasobnik operandu 6: arraylength // vypocitat delku pole (pocet prvku) 7: if_icmpge 41 // podminka na zacatku iterace (vnejsi smycka) 10: iconst_0 // konstanta 0 na vrchol zasobniku operandu 11: istore_3 // lokalni promenna pouzita ve funkci pocitadla vnitrni smycky 12: iload_3 // hodnota pocitadla na zasobnik 13: aload_0 // ulozit referenci na pole na zasobnik operandu 14: iload_2 // pocitadlo vnejsi smycky ve funkci indexu 15: aaload // nacist i-ty prvek "vnejsiho" pole 16: arraylength // spocitat delku sub-pole 17: if_icmpge 35 // podminka na zacatku iterace (vnitrni smycka) 20: iload_1 // hodnota sumy na zasobnik 21: aload_0 // ulozit referenci na pole na zasobnik operandu 22: iload_2 23: aaload // nacist sub-pole 24: iload_3 25: saload // nacist jiz konkretni prvek pole 26: iadd // pricist k sume 27: i2s // konverze mezivysledku 28: istore_1 // ulozit mezivysledek 29: iinc 3, 1 // zvysit hodnotu pocitadla vnitrni smycky o jednicku 32: goto 12 // skok na zacatek vnitrni programove smycky 35: iinc 2, 1 // zvysit hodnotu pocitadla vnejsi smycky o jednicku 38: goto 4 // skok na zacatek vnejsi programove smycky 41: iload_1 // suma 42: ireturn // vratit hodnotu sumy Operace s prvky pole typu int[][] static int sum(int[][]); Code: 0: iconst_0 // konstanta 0 na vrchol zasobniku operandu 1: istore_1 // lokalni promenna pouzita ve funkci sumy 2: iconst_0 // konstanta 0 na vrchol zasobniku operandu 3: istore_2 // lokalni promenna pouzita ve funkci pocitadla vnejsi smycky 4: iload_2 // hodnota pocitadla na zasobnik 5: aload_0 // ulozit referenci na pole na zasobnik operandu 6: arraylength // vypocitat delku pole (pocet prvku) 7: if_icmpge 40 // podminka na zacatku iterace (vnejsi smycka) 10: iconst_0 // konstanta 0 na vrchol zasobniku operandu 11: istore_3 // lokalni promenna pouzita ve funkci pocitadla vnitrni smycky 12: iload_3 // hodnota pocitadla na zasobnik 13: aload_0 // ulozit referenci na pole na zasobnik operandu 14: iload_2 // pocitadlo vnejsi smycky ve funkci indexu 15: aaload // nacist i-ty prvek "vnejsiho" pole 16: arraylength // spocitat delku sub-pole 17: if_icmpge 34 // podminka na zacatku iterace (vnitrni smycka) 20: iload_1 // hodnota sumy na zasobnik 21: aload_0 // ulozit referenci na pole na zasobnik operandu 22: iload_2 23: aaload // nacist sub-pole 24: iload_3 25: iaload // nacist jiz konkretni prvek pole 26: iadd 27: istore_1 // ulozit mezivysledek 28: iinc 3, 1 // zvysit hodnotu pocitadla vnitrni smycky o jednicku 31: goto 12 // skok na zacatek vnitrni programove smycky 34: iinc 2, 1 // zvysit hodnotu pocitadla vnejsi smycky o jednicku 37: goto 4 // skok na zacatek vnejsi programove smycky 40: iload_1 // suma 41: ireturn // vratit hodnotu sumy Operace s prvky pole typu long[][] static long sum(long[][]); Code: 0: lconst_0 // konstanta 0 na vrchol zasobniku operandu 1: lstore_1 // lokalni promenna pouzita ve funkci sumy 2: iconst_0 // konstanta 0 na vrchol zasobniku operandu 3: istore_3 // lokalni promenna pouzita ve funkci pocitadla vnejsi smycky 4: iload_3 // hodnota pocitadla na zasobnik 5: aload_0 // ulozit referenci na pole na zasobnik operandu 6: arraylength // vypocitat delku pole (pocet prvku) 7: if_icmpge 43 // podminka na zacatku iterace (vnejsi smycka) 10: iconst_0 // konstanta 0 na vrchol zasobniku operandu 11: istore 4 // lokalni promenna pouzita ve funkci pocitadla vnitrni smycky 13: iload 4 // hodnota pocitadla na zasobnik 15: aload_0 // ulozit referenci na pole na zasobnik operandu 16: iload_3 // pocitadlo vnejsi smycky ve funkci indexu 17: aaload // nacist i-ty prvek "vnejsiho" pole 18: arraylength // spocitat delku sub-pole 19: if_icmpge 37 // podminka na zacatku iterace (vnitrni smycka) 22: lload_1 // hodnota sumy na zasobnik 23: aload_0 24: iload_3 25: aaload // nacist i-ty prvek "vnejsiho" pole 26: iload 4 28: laload // nacist jiz konkretni prvek pole 29: ladd 30: lstore_1 // ulozit mezivysledek 31: iinc 4, 1 // zvysit hodnotu pocitadla vnitrni smycky o jednicku 34: goto 13 // skok na zacatek vnitrni programove smycky 37: iinc 3, 1 // zvysit hodnotu pocitadla vnejsi smycky o jednicku 40: goto 4 // skok na zacatek vnejsi programove smycky 43: lload_1 // suma 44: lreturn // vratit hodnotu sumy Operace s prvky pole typu float[][] static float sum(float[][]); Code: 0: fconst_0 // konstanta 0 na vrchol zasobniku operandu 1: fstore_1 // lokalni promenna pouzita ve funkci sumy 2: iconst_0 // konstanta 0 na vrchol zasobniku operandu 3: istore_2 // lokalni promenna pouzita ve funkci pocitadla vnejsi smycky 4: iload_2 // hodnota pocitadla na zasobnik 5: aload_0 // ulozit referenci na pole na zasobnik operandu 6: arraylength // vypocitat delku pole (pocet prvku) 7: if_icmpge 40 // podminka na zacatku iterace (vnejsi smycka) 10: iconst_0 // konstanta 0 na vrchol zasobniku operandu 11: istore_3 // lokalni promenna pouzita ve funkci pocitadla vnitrni smycky 12: iload_3 // hodnota pocitadla na zasobnik 13: aload_0 // ulozit referenci na pole na zasobnik operandu 14: iload_2 // pocitadlo vnejsi smycky ve funkci indexu 15: aaload // nacist i-ty prvek "vnejsiho" pole 16: arraylength // spocitat delku sub-pole 17: if_icmpge 34 // podminka na zacatku iterace (vnitrni smycka) 20: fload_1 // hodnota sumy na zasobnik 21: aload_0 22: iload_2 23: aaload // nacist i-ty prvek "vnejsiho" pole 24: iload_3 25: faload // nacist jiz konkretni prvek pole 26: fadd 27: fstore_1 // ulozit mezivysledek 28: iinc 3, 1 // zvysit hodnotu pocitadla vnitrni smycky o jednicku 31: goto 12 // skok na zacatek vnitrni programove smycky 34: iinc 2, 1 // zvysit hodnotu pocitadla vnejsi smycky o jednicku 37: goto 4 // skok na zacatek vnejsi programove smycky 40: fload_1 // suma 41: freturn // vratit hodnotu sumy Operace s prvky pole typu double[][] static double sum(double[][]); Code: 0: dconst_0 // konstanta 0 na vrchol zasobniku operandu 1: dstore_1 // lokalni promenna pouzita ve funkci sumy 2: iconst_0 // konstanta 0 na vrchol zasobniku operandu 3: istore_3 // lokalni promenna pouzita ve funkci pocitadla vnejsi smycky 4: iload_3 // hodnota pocitadla na zasobnik 5: aload_0 // ulozit referenci na pole na zasobnik operandu 6: arraylength // vypocitat delku pole (pocet prvku) 7: if_icmpge 43 // podminka na zacatku iterace (vnejsi smycka) 10: iconst_0 // konstanta 0 na vrchol zasobniku operandu 11: istore 4 // lokalni promenna pouzita ve funkci pocitadla vnitrni smycky 13: iload 4 // hodnota pocitadla na zasobnik 15: aload_0 // ulozit referenci na pole na zasobnik operandu 16: iload_3 // pocitadlo vnejsi smycky ve funkci indexu 17: aaload // nacist i-ty prvek "vnejsiho" pole 18: arraylength // spocitat delku sub-pole 19: if_icmpge 37 // podminka na zacatku iterace (vnitrni smycka) 22: dload_1 // hodnota sumy na zasobnik 23: aload_0 24: iload_3 25: aaload // nacist i-ty prvek "vnejsiho" pole 26: iload 4 28: daload // nacist jiz konkretni prvek pole 29: dadd 30: dstore_1 // ulozit mezivysledek 31: iinc 4, 1 // zvysit hodnotu pocitadla vnitrni smycky o jednicku 34: goto 13 // skok na zacatek vnitrni programove smycky 37: iinc 3, 1 // zvysit hodnotu pocitadla vnejsi smycky o jednicku 40: goto 4 // skok na zacatek vnejsi programove smycky 43: dload_1 // suma 44: dreturn // vratit hodnotu sumy Operace s prvky pole typu Object[][] static int sum(java.lang.Object[][]); Code: 0: iconst_0 // konstanta 0 na vrchol zasobniku operandu 1: istore_1 // lokalni promenna pouzita ve funkci sumy 2: iconst_0 // konstanta 0 na vrchol zasobniku operandu 3: istore_2 // lokalni promenna pouzita ve funkci pocitadla vnejsi smycky 4: iload_2 // hodnota pocitadla na zasobnik 5: aload_0 // ulozit referenci na pole na zasobnik operandu 6: arraylength // vypocitat delku pole (pocet prvku) 7: if_icmpge 43 // podminka na zacatku iterace (vnejsi smycka) 10: iconst_0 // konstanta 0 na vrchol zasobniku operandu 11: istore_3 // lokalni promenna pouzita ve funkci pocitadla vnitrni smycky 12: iload_3 // hodnota pocitadla na zasobnik 13: aload_0 // ulozit referenci na pole na zasobnik operandu 14: iload_2 // pocitadlo vnejsi smycky ve funkci indexu 15: aaload // nacist i-ty prvek "vnejsiho" pole 16: arraylength // spocitat delku sub-pole 17: if_icmpge 37 // podminka na zacatku iterace (vnitrni smycka) 20: iload_1 // hodnota sumy na zasobnik 21: aload_0 22: iload_2 23: aaload // nacist i-ty prvek "vnejsiho" pole 24: iload_3 25: aaload // nacist jiz konkretni prvek pole 26: invokevirtual #2; // Method java/lang/Object.hashCode:()I 29: iadd 30: istore_1 // ulozit mezivysledek 31: iinc 3, 1 // zvysit hodnotu pocitadla vnitrni smycky o jednicku 34: goto 12 // skok na zacatek vnitrni programove smycky 37: iinc 2, 1 // zvysit hodnotu pocitadla vnejsi smycky o jednicku 40: goto 4 // skok na zacatek vnejsi programove smycky 43: iload_1 // suma 44: ireturn // vratit hodnotu sumy
5. Přístup k prvkům tabulek v Lua VM
Bajtkód využívaný virtuálním strojem programovacího jazyka Lua je založen, jak jsme se již dozvěděli v předcházejících částech tohoto seriálu, na registrech, do nichž jsou ukládány jak parametry funkcí, tak i lokální proměnné. Navíc se u většiny instrukcí bajtkódu používají parametry, které jsou součástí instrukčního slova. Nejinak je tomu i u instrukcí určených pro čtení a zápis hodnot do polí, přesněji řečeno do tabulek. Jedná se o instrukce nazvané GETTABLE a SETTABLE. Instrukce GETTABLE má v instrukčním slovu uloženy indexy tří registrů – index registru pro uložení výsledku, index registru s referencí na tabulku a index registru s uloženým pořadím čteného prvku. U instrukce SETTABLE je taktéž použita trojice indexů, které obsahují registr s referencí na pole, registr s indexem zapisovaného prvku a konečně registr s novou hodnotou prvku:
# | Instrukce | Operandy | Prováděná operace |
---|---|---|---|
1 | GETTABLE | registr pole index | přečtení prvku z tabulky |
2 | SETTABLE | pole index hodnota | zápis prvku do tabulky |
3 | LEN | registr pole | vrací délku pole |
6. Demonstrační příklad Test21.lua: přístup k prvkům jednorozměrného pole (tabulky)
Opět se, podobně jako v případě programovacího jazyka Java, podívejme na jednoduchý demonstrační příklad, v němž se provádí čtení i zápis do tabulek. Na rozdíl od Javy nemusíme v případě programovacího jazyka Lua přetěžovat funkce/metody, protože bajtkód Lua VM používá dynamické typování. Z tohoto důvodu je demonstrační příklad mnohem kratší:
-- -- Pristup k prvkum (jednorozmerne) tabulky. -- -- -- Nejjednodussi operace - precteni hodnoty prvku z pole/tabulky. -- function readItem(array, index) return array[index] end -- -- Dalsi jednoducha operace - zapis hodnoty prvku do pole/tabulky. -- function writeItem(array, index, value) array[index] = value end -- -- Zvyseni hodnoty vsech prvku pole/tabulky o jednicku. -- function modify(array) for i = 1, #array do array[i] = array[i] + 1 end end -- -- Tisk vsech prvku pole/tabulky. -- function printArray(array) for i = 1, #array do print(readItem(array, i)) end end -- -- Test. -- function main() local array = {1, 2, 3} print("Original array:") printArray(array) print() writeItem(array, 2, 42) print("2nd item have been changed:") printArray(array) print() modify(array) print("Modified array:") printArray(array) print() end main()
Bajtkód funkce readItem() je velmi jednoduchý, protože obsahuje pouze trojici instrukcí, kde je navíc poslední instrukce ve skutečnosti nadbytečná. Přečtení prvku na daném indexu je provedeno jedinou instrukcí GETTABLE:
function <Test21.lua:10,12> (3 instructions at 0x9da4c88) 2 params, 3 slots, 0 upvalues, 2 locals, 0 constants, 0 functions 1 [11] GETTABLE 2 0 1 // prime precteni prvku z tabulky (parametr 0) s indexem (parametr 1) 2 [11] RETURN 2 2 // vraceni prectene hodnoty prvku 3 [12] RETURN 0 1 // automaticky vkladany navrat z funkce
Funkce writeItem() určená pro změnu hodnoty vybraného prvku tabulky je dokonce ještě jednodušší, protože všechnu funkcionalitu vykonává jediná instrukce SETTABLE:
function <Test21.lua:19,21> (2 instructions at 0x9da4f08) 3 params, 3 slots, 0 upvalues, 3 locals, 0 constants, 0 functions 1 [20] SETTABLE 0 1 2 // primy zapis prvku do tabulky (parametr 0) s indexem (parametr 1) 2 [21] RETURN 0 1 // automaticky vkladany navrat z funkce
Poněkud složitější je bajtkód funkce, v níž se zvyšují hodnoty všech prvků tabulky o jedničku. Nejprve se zjistí délka tabulky instrukcí LEN a posléze je s využitím FORPREP a FORLOOP realizována počítaná programová smyčka:
function <Test21.lua:28,32> (9 instructions at 0x9da50f8) 1 param, 6 slots, 0 upvalues, 5 locals, 1 constant, 0 functions 1 [29] LOADK 1 -1 ; 1 // pocatecni hodnota pocitadla pro for 2 [29] LEN 2 0 // vraci se delka tabulky 3 [29] LOADK 3 -1 ; 1 // priprava smycky for 4 [29] FORPREP 1 3 ; to 8 // zacatek smycky for 5 [30] GETTABLE 5 0 4 // cteni puvodni hodnoty prvku 6 [30] ADD 5 5 -1 ; - 1 // pricteni jednicky (-1 je index konstanty) 7 [30] SETTABLE 0 4 5 // zapis nove hodnoty prvku 8 [29] FORLOOP 1 -4 ; to 5 // iterace na konci smycky 9 [32] RETURN 0 1 // automaticky vkladany navrat z funkce
Na této trojici přeložených funkcí můžeme vidět, že bajtkód Lua VM je na jedné straně velmi flexibilní (dynamické typování) a na straně druhé i do značné míry kompaktní, zejména v porovnání s JVM.
7. Demonstrační příklad Test22.lua: přístup k prvkům dvourozměrného pole (tabulky)
Další demonstrační příklad nazvaný Test22.lua obsahuje trojici funkcí, které přistupují k prvkům dvourozměrné tabulky, která je chápána jako běžná jednorozměrná tabulka obsahující jako své prvky další tabulky:
-- -- Pristup k prvkum tabulky. -- -- -- Nejjednodussi operace - precteni hodnoty prvku z dvourozmerne tabulky/pole. -- function readItem(array, index1, index2) return array[index1][index2] end -- -- Dalsi jednoducha operace - zapis hodnoty prvku do dvourozmerne tabulky. -- function writeItem(array, index1, index2, value) array[index1][index2] = value end -- -- Vypocet sumy vsech prvku dvourozmerneho pole. -- function sum(array) local sum = 0 for i = 1, #array do for j = 1, #array[i] do sum = sum + array[i][j] end end return sum end -- -- Tisk vsech prvku dvourozmerneho pole. -- function printArray(array) for i = 1, #array do for j = 1, #array[i] do io.write(readItem(array, i, j)) io.write(' ') end print() end end -- -- Test. -- function main() local array = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}} printArray(array) print("Sum = " .. sum(array)) print() writeItem(array, 2, 2, 0) printArray(array) print("Sum = " .. sum(array)) end main()
V bajtkódu funkce readItem() vidíme dvojici instrukcí GETTABLE použitých pro přečtení i-té podtabulky a j-tého prvku z této podtabulky:
function <Test22.lua:10,12> (4 instructions at 0x970cc88) 3 params, 4 slots, 0 upvalues, 3 locals, 0 constants, 0 functions 1 [11] GETTABLE 3 0 1 // precteni tabulky (parametr 0) s indexem (parametr 1) 2 [11] GETTABLE 3 3 2 // cteni hodnoty prvku z podtabulky 3 [11] RETURN 3 2 // vraceni prectene hodnoty prvku 4 [12] RETURN 0 1 // automaticky vkladany navrat z funkce
Bajtkód funkce writeItem() nejprve získá pomocí instrukce GETTABLE i-tou podtabulku a posléze do ní zapíše j-tý prvek instrukcí SETTABLE:
function <Test22.lua:19,21> (3 instructions at 0x970cf28) 4 params, 5 slots, 0 upvalues, 4 locals, 0 constants, 0 functions 1 [20] GETTABLE 4 0 1 // precteni tabulky (parametr 0) s indexem (parametr 1) 2 [20] SETTABLE 4 2 3 // primy zapis prvku do tabulky 3 [21] RETURN 0 1 // automaticky vkladany navrat z funkce
Získání sumy všech prvků dvourozměrné tabulky (tj. tabulky s podtabulkami) opět vyžaduje použití vnořených programových smyček typu for, což je ostatně patrné i z následujícího výpisu:
function <Test22.lua:28,36> (17 instructions at 0x970d148) 1 param, 11 slots, 0 upvalues, 10 locals, 2 constants, 0 functions 1 [29] LOADK 1 -1 ; 0 // registr cislo 1 obsahuje pocitanou sumu 2 [30] LOADK 2 -2 ; 1 3 [30] LEN 3 0 // delka tabulky (pocet podtabulek) 4 [30] LOADK 4 -2 ; 1 // vsechny parametry vnejsi smycky jsou nastaveny 5 [30] FORPREP 2 9 ; to 15 // zacatek vnejsi smycky for 6 [31] LOADK 6 -2 ; 1 7 [31] GETTABLE 7 0 5 // cteni podtabulky 8 [31] LEN 7 7 // a vypocet jeji delky 9 [31] LOADK 8 -2 ; 1 // vsechny parametry vnitrni smycky jsou nastaveny 10 [31] FORPREP 6 3 ; to 14 // zacatek vnitrni smycky for 11 [32] GETTABLE 10 0 5 // cteni podtabulky 12 [32] GETTABLE 10 10 9 // cteni prvku z podtabulky do registru 10 13 [32] ADD 1 1 10 // pricteni hodnoty k sume 14 [31] FORLOOP 6 -4 ; to 11 // iterace na konci vnitrni smycky 15 [30] FORLOOP 2 -10 ; to 6 // iterace na konci vnejsi smycky 16 [35] RETURN 1 2 17 [36] RETURN 0 1 // automaticky vkladany navrat z funkce
Opět je patrná kompaktnost bajtkódu Lua VM.
8. Přístup k prvkům seznamů a n-tic v Python VM
V programovacím jazyce Python se namísto polí či tabulek využívá dvojice složených datových typů. Jedná se o seznamy (list), které jsou modifikovatelné, tj. lze do nich vkládat nové prvky, měnit hodnoty prvků či prvky vyjímat. Seznamy jsou v tomto pojetí velmi flexibilním datovým typem, jenž může plnohodnotně nahradit další složené datové typy, jakými jsou zásobník (stack) či fronta (queue). Kromě seznamů lze v Pythonu používat i n-tice (tuple), které jsou na rozdíl od seznamů neměnitelné, tj. immutable. Díky této vlastnosti lze n-tice použít například ve funkci klíčů v asociativních polích atd. Jakým způsobem je realizován přístup k prvkům seznamů i n-tic si podrobněji řekneme v navazující části tohoto seriálu. Kromě toho si příště popíšeme i další „vychytávky“ Lua VM a Python VM, včetně využití již zmíněných asociativních polí, která jsou velmi flexibilním složeným datovým typem.
09. Repositář se zdrojovými kódy všech dnešních demonstračních příkladů
Všechny dnes popsané a využité demonstrační příklady (naprogramované v Javě, Lue i Pythonu) 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 příkladů naleznete v tabulce pod tímto odstavcem:
10. Odkazy na Internetu
- Programming in Lua (first edition)
http://www.lua.org/pil/contents.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 - Python break, continue and pass Statements
http://www.tutorialspoint.com/python/python_loop_control.htm - 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 - 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 - 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 - 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/ - O-code
http://en.wikipedia.org/wiki/O-code_machine - BCPL
http://en.wikipedia.org/wiki/BCPL - The BCPL Cintcode System and Cintpos User Guide by Martin Richards
http://www.cl.cam.ac.uk/users/mr/bcplman.pdf - Bootstrapping the BCPL Compiler using INTCODE
http://www.gtoal.com/languages/bcpl/amiga/bcpl/booting.txt - p-code machine
http://en.wikipedia.org/wiki/P-code_machine - ucsd-psystem-vm 0.11 (a portable virtual machine for the UCSD p-System)
http://ucsd-psystem-vm.sourceforge.net/ - Introduction to Smalltalk bytecodes
http://marianopeck.wordpress.com/2011/05/21/introduction-to-smalltalk-bytecodes/ - Audio File Formats.
http://sox.sourceforge.net/AudioFormats-11.html - TestSounds.com: pure digital sounds to test your audio
http://www.testsounds.com/ - Test Tones (20hz – 20khz)
http://mdf1.tripod.com/test-tones.html - WAV (Wikipedia)
http://en.wikipedia.org/wiki/WAV - WAVE PCM soundfile format
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ - Audio Interchange File Format
http://en.wikipedia.org/wiki/Aiff - Musical Instrument Digital Interface,
http://en.wikipedia.org/wiki/Musical_Instrument_Digital_Interface - A MIDI Pedalboard Encode,
http://www.pykett.org.uk/a_midi_pedalboard_encoder.htm - MIDI Note Number, Frequency Table,
http://tonalsoft.com/pub/news/pitch-bend.aspx - Note names, MIDI numbers and frequencies,
http://www.phys.unsw.edu.au/jw/notes.html - The MIDI Specification,
http://www.gweep.net/~prefect/eng/reference/protocol/midispec.html - Essentials of the MIDI protocol,
http://ccrma.stanford.edu/~craig/articles/linuxmidi/misc/essenmidi.html - General MIDI,
http://en.wikipedia.org/wiki/General_MIDI - Obecné MIDI (General MIDI),
http://www-kiv.zcu.cz/~herout/html_sbo/midi/5.html - Custom Chips: Paula
http://www.amiga-hardware.com/showhardware.cgi?HARDID=1460 - Big Book of Amiga Hardware
http://www.amiga-resistance.info/bboahfaq/ - Amiga Hardware Database
http://amiga.resource.cx/ - ExoticA
http://www.exotica.org.uk/wiki/Main_Page - The absolute basics of Amiga audio
http://www.sufo.estates.co.uk/amiga/amimus.html - Wikipedia: Tracker
http://en.wikipedia.org/wiki/Tracker - Wikipedia: Trackers
http://en.wikipedia.org/wiki/Trackers - Ultimate Soundtracker
http://en.wikipedia.org/wiki/Ultimate_Soundtracker - Protracker
http://en.wikipedia.org/wiki/ProTracker - Impulse Tracker
http://en.wikipedia.org/wiki/Impulse_Tracker - Scream Tracker
http://en.wikipedia.org/wiki/ScreamTracker - MikMod for Java
http://jmikmod.berlios.de/ - List of audio trackers
http://en.wikipedia.org/wiki/List_of_audio_trackers - Wikipedia: Module File
http://en.wikipedia.org/wiki/Module_file - Wikipedia: Chiptune
http://en.wikipedia.org/wiki/Chiptune - SDL_mixer 2.0
http://www.libsdl.org/projects/SDL_mixer/ - SDLJava: package sdljava.ttf
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/package-summary.html#package_description - SDLJava: class sdljava.ttf.SDLTTF
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/SDLTTF.html - SDLJava: class sdljava.ttf.SDLTrueTypeFont
http://sdljava.sourceforge.net/docs/api/sdljava/ttf/SDLTrueTypeFont.html - SDL_ttf Documentation
http://www.libsdl.org/projects/SDL_ttf/docs/ - SDL_ttf 2.0 (není prozatím součástí SDLJava)
http://www.libsdl.org/projects/SDL_ttf/ - SDL_ttf doc
http://www.libsdl.org/projects/SDL_ttf/docs/SDL_ttf_frame.html - SDL 1.2 Documentation: SDL_Surface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsurface.html - SDL 1.2 Documentation: SDL_PixelFormat
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlpixelformat.html - SDL 1.2 Documentation: SDL_LockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdllocksurface.html - SDL 1.2 Documentation: SDL_UnlockSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlunlocksurface.html - SDL 1.2 Documentation: SDL_LoadBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlloadbmp.html - SDL 1.2 Documentation: SDL_SaveBMP
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlsavebmp.html - SDL 1.2 Documentation: SDL_BlitSurface
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlblitsurface.html - SDL 1.2 Documentation: SDL_VideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlvideoinfo.html - SDL 1.2 Documentation: SDL_GetVideoInfo
http://www.libsdl.org/release/SDL-1.2.15/docs/html/sdlgetvideoinfo.html - glDrawArrays
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArrays.xml - glDrawElements
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElements.xml - glDrawArraysInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawArraysInstanced.xml - glDrawElementsInstanced
http://www.opengl.org/sdk/docs/man4/xhtml/glDrawElementsInstanced.xml - Root.cz: Seriál Grafická knihovna OpenGL
http://www.root.cz/serialy/graficka-knihovna-opengl/ - Root.cz: Seriál Tvorba přenositelných grafických aplikací využívajících knihovnu GLUT
http://www.root.cz/serialy/tvorba-prenositelnych-grafickych-aplikaci-vyuzivajicich-knihovnu-glut/ - Best Practices for Working with Vertex Data
https://developer.apple.com/library/ios/documentation/3ddrawing/conceptual/opengles_programmingguide/TechniquesforWorkingwithVertexData/TechniquesforWorkingwithVertexData.html - Class BufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/image/BufferStrategy.html - Class Graphics
http://docs.oracle.com/javase/1.5.0/docs/api/java/awt/Graphics.html - Double Buffering and Page Flipping
http://docs.oracle.com/javase/tutorial/extra/fullscreen/doublebuf.html - BufferStrategy and BufferCapabilities
http://docs.oracle.com/javase/tutorial/extra/fullscreen/bufferstrategy.html - Java:Tutorials:Double Buffering
http://content.gpwiki.org/index.php/Java:Tutorials:Double_Buffering - Double buffer in standard Java AWT
http://www.codeproject.com/Articles/2136/Double-buffer-in-standard-Java-AWT - Java 2D: Hardware Accelerating – Part 1 – Volatile Images
http://www.javalobby.org/forums/thread.jspa?threadID=16840&tstart=0 - Java 2D: Hardware Accelerating – Part 2 – Buffer Strategies
http://www.javalobby.org/java/forums/t16867.html - How does paintComponent work?
http://stackoverflow.com/questions/15544549/how-does-paintcomponent-work - A Swing Architecture Overview
http://www.oracle.com/technetwork/java/architecture-142923.html - Class javax.swing.JComponent
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html - Class java.awt.Component
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html - Class java.awt.Component.BltBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.BltBufferStrategy.html - Class java.awt.Component.FlipBufferStrategy
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.FlipBufferStrategy.html - Metoda java.awt.Component.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/java/awt/Component.html#isDoubleBuffered() - Metoda javax.swing.JComponent.isDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#isDoubleBuffered() - Metoda javax.swing.JComponent.setDoubleBuffered()
http://docs.oracle.com/javase/6/docs/api/javax/swing/JComponent.html#setDoubleBuffered(boolean) - Javadoc – třída GraphicsDevice
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsDevice.html - Javadoc – třída GraphicsEnvironment
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsEnvironment.html - Javadoc – třída GraphicsConfiguration
http://docs.oracle.com/javase/7/docs/api/java/awt/GraphicsConfiguration.html - Javadoc – třída DisplayMode
http://docs.oracle.com/javase/7/docs/api/java/awt/DisplayMode.html - Lesson: Full-Screen Exclusive Mode API
http://docs.oracle.com/javase/tutorial/extra/fullscreen/ - Full-Screen Exclusive Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/exclusivemode.html - Display Mode
http://docs.oracle.com/javase/tutorial/extra/fullscreen/displaymode.html - Using the Full-Screen Exclusive Mode API in Java
http://www.developer.com/java/other/article.php/3609776/Using-the-Full-Screen-Exclusive-Mode-API-in-Java.htm - 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 - MultiMedia eXtensions
http://softpixel.com/~cwright/programming/simd/mmx.phpi - SSE (Streaming SIMD Extentions)
http://www.songho.ca/misc/sse/sse.html - Timothy A. Chagnon: SSE and SSE2
http://www.cs.drexel.edu/~tc365/mpi-wht/sse.pdf - Intel corporation: Extending the Worldr's Most Popular Processor Architecture
http://download.intel.com/technology/architecture/new-instructions-paper.pdf - SIMD architectures:
http://arstechnica.com/old/content/2000/03/simd.ars/ - 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